springboot openfeign Sentinel统一降级处理

背景

openfeign降级常规操作如下:
springboot openfeign Sentinel统一降级处理_第1张图片
此种方式太过于麻烦,每一个方法都要写一个降级逻辑,并且降级逻辑大多是雷同的。

目标

提供默认的降级方式,若openfeign未指定FallbackFactory则走默认降级方式,否则就走自定义的FallbackFactory降级。文末附实现代码下载

版本

  1. springcloud
<dependency>
	<groupId>org.springframework.cloudgroupId>
	<artifactId>spring-cloud-dependenciesartifactId>
	<version>2021.0.1version>
	<type>pomtype>
	<scope>importscope>
dependency>
  1. sentinel
<dependency>
	<groupId>com.alibaba.cloudgroupId>
	<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
	<version>2021.0.1.0version>
dependency>

实现

  • 查看spring-cloud-starter-alibaba-sentinel降级源码
    springboot openfeign Sentinel统一降级处理_第2张图片
    从上述源码可以看到若不存在自定义的fallbackFactory,则直接抛异常,我们需要做的就是在抛异常之前插入我们的默认降级逻辑。这里有如下两种插入代码块方式:
    1:Java ASM字节码修改技术插入,过往文章:https://blog.csdn.net/qq_41633199/article/details/117160642
    2:参照SentinelInvocationHandler源码新建一个java文件手动写入代码块,再替换sentinel的默认实现
    这里我选择方法2.
  • 添加默认降级逻辑
    • 参照SentinelInvocationHandler.java源码新建一个java文件DegradeSentinelInvocationHandler.java
    • 手动写入默认降级方法
if (fallbackFactory != null) {
	try {
		Object fallbackResult = fallbackMethodMap.get(method)
			.invoke(fallbackFactory.create(ex), args);
		return fallbackResult;
	}
	catch (IllegalAccessException e) {
		// shouldn't happen as method is public due to being an
		// interface
		throw new AssertionError(e);
	}
	catch (InvocationTargetException e) {
		throw new AssertionError(e.getCause());
	}
}
else {
	//没有自定义降级处理,并且返回格式是R,则进行统一降级处理
	if (method.getReturnType().equals(R.class)) {
		log.warn(ex.getMessage(), ex);
		String failMsg = ofNullable(ex).map(Throwable::getMessage).filter(CharSequenceUtil::isNotEmpty).orElse("系统异常,请联系管理员");
		return R.error(failMsg);
	}
	throw ex;
}
  • 参照com.alibaba.cloud.sentinel.feign.SentinelFeign.java新建DegradeSentinelFeign.java构建Feign对象
    核心代码:
@Override
public Feign build() {
	super.invocationHandlerFactory(new InvocationHandlerFactory() {
		@Override
		public InvocationHandler create(Target target,
										Map<Method, MethodHandler> dispatch) {
			GenericApplicationContext gctx = (GenericApplicationContext) DegradeSentinelFeign.Builder.this.applicationContext;
			BeanDefinition def = gctx.getBeanDefinition(target.type().getName());

			/*
			 * Due to the change of the initialization sequence,
			 * BeanFactory.getBean will cause a circular dependency. So
			 * FeignClientFactoryBean can only be obtained from BeanDefinition
			 */
			FeignClientFactoryBean feignClientFactoryBean = (FeignClientFactoryBean) def
				.getAttribute("feignClientsRegistrarFactoryBean");

			Class fallback = feignClientFactoryBean.getFallback();
			Class fallbackFactory = feignClientFactoryBean.getFallbackFactory();
			String beanName = feignClientFactoryBean.getContextId();
			if (!StringUtils.hasText(beanName)) {
				beanName = (String) getFieldValue(feignClientFactoryBean, "name");
			}

			Object fallbackInstance;
			FallbackFactory fallbackFactoryInstance;
			// check fallback and fallbackFactory properties
			if (void.class != fallback) {
				fallbackInstance = getFromContext(beanName, "fallback", fallback,
					target.type());
				return new DegradeSentinelInvocationHandler(target, dispatch,
					new FallbackFactory.Default(fallbackInstance));
			}
			if (void.class != fallbackFactory) {
				fallbackFactoryInstance = (FallbackFactory) getFromContext(
					beanName, "fallbackFactory", fallbackFactory,
					FallbackFactory.class);
				return new DegradeSentinelInvocationHandler(target, dispatch,
					fallbackFactoryInstance);
			}

			return new DegradeSentinelInvocationHandler(target, dispatch);
		}

		private Object getFromContext(String name, String type,
									  Class fallbackType, Class targetType) {
			Object fallbackInstance = feignContext.getInstance(name,
				fallbackType);
			if (fallbackInstance == null) {
				throw new IllegalStateException(String.format(
					"No %s instance of type %s found for feign client %s",
					type, fallbackType, name));
			}

			if (!targetType.isAssignableFrom(fallbackType)) {
				throw new IllegalStateException(String.format(
					"Incompatible %s instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
					type, fallbackType, targetType, name));
			}
			return fallbackInstance;
		}
	});

	super.contract(new SentinelContractHolder(contract));
	return super.build();
}
  • 参照com.alibaba.cloud.sentinel.feign.SentinelFeignAutoConfiguration.java新建DegradeSentinelFeignAutoConfiguration.java替换Sentinel自带降级处理逻辑
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({SphU.class, Feign.class, SentinelFeignAutoConfiguration.class})
@AutoConfigureBefore(SentinelFeignAutoConfiguration.class)
public class DegradeSentinelFeignAutoConfiguration {

    @Bean
    @Scope("prototype")
    @ConditionalOnMissingBean
    @ConditionalOnProperty(name = "feign.sentinel.enabled")
    public Feign.Builder feignSentinelBuilder() {
        return DegradeSentinelFeign.builder();
    }
}
  • 在spring.factories文件指定自定义降级配置类
    springboot openfeign Sentinel统一降级处理_第3张图片

完整代码下载

springboot openfeign Sentinel统一降级处理实现代码

你可能感兴趣的:(spring,boot,sentinel,java)