springboot openfeign Sentinel统一降级处理

背景

openfeign降级常规操作如下:
在这里插入图片描述
此种方式太过于麻烦,每一个方法都要写一个降级逻辑,并且降级逻辑大多是雷同的。

目标

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

版本

  1. springcloud
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-dependencies</artifactId>
	<version>2021.0.1</version>
	<type>pom</type>
	<scope>import</scope>
</dependency>
  1. sentinel
<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
	<version>2021.0.1.0</version>
</dependency>

实现

  • 查看spring-cloud-starter-alibaba-sentinel降级源码
    在这里插入图片描述
    从上述源码可以看到若不存在自定义的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统一降级处理实现代码

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值