查看原文
其他

扩展Spring Cloud Feign 实现自动降级

giegie SpringForAll社区 2020-10-17

giegie

读完需要

9分钟

速读仅需3分钟



   

自动降级目的

在Spring Cloud 使用feign 的时候,需要明确指定fallback 策略,不然会提示错误。先来看默认的feign service 是要求怎么做的。feign service 定义一个 factory 和 fallback 的类

  1. @FeignClient(value = ServiceNameConstants.UMPS_SERVICE, fallbackFactory = RemoteLogServiceFallbackFactory.class)

  2. public interface RemoteLogService {}


但是我们大多数情况的feign 降级策略为了保证幂等都会很简单,输出错误日志即可。类似如下代码,在企业中开发非常不方便

  1. @Slf4j

  2. @Component

  3. public class RemoteLogServiceFallbackImpl implements RemoteLogService {

  4.    @Setter

  5.    private Throwable cause;



  6.    @Override

  7.    public R<Boolean> saveLog(SysLog sysLog, String from) {

  8.        log.error("feign 插入日志失败", cause);

  9.        return null;

  10.    }

  11. }



   

自动降级效果

  1. @FeignClient(value = ServiceNameConstants.UMPS_SERVICE)

  2. public interface RemoteLogService {}


  • Feign Service 完成同样的降级错误输出

  • FeignClient 中无需定义无用的fallbackFactory

  • FallbackFactory 也无需注册到Spring 容器中

代码变化,去掉FeignClient 指定的降级工厂

代码变化,删除降级相关的代码



   

核心源码

1. 注入我们个性化后的Feign

  1. @Configuration

  2. @ConditionalOnClass({HystrixCommand.class, HystrixFeign.class})

  3. protected static class HystrixFeignConfiguration {

  4.    @Bean

  5.    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

  6.    @ConditionalOnProperty("feign.hystrix.enabled")

  7.    public Feign.Builder feignHystrixBuilder(FeignContext feignContext) {

  8.        return PigxHystrixFeign.builder(feignContext)

  9.                .decode404()

  10.                .errorDecoder(new PigxFeignErrorDecoder());

  11.    }

  12. }

2. PigxHystrixFeign.target 方法是根据@FeignClient 注解生成代理类的过程,注意注释

  1. @Override

  2. public <T> T target(Target<T> target) {

  3.    Class<T> targetType = target.type();

  4.    FeignClient feignClient = AnnotatedElementUtils.getMergedAnnotation(targetType, FeignClient.class);

  5.    String factoryName = feignClient.name();

  6.    SetterFactory setterFactoryBean = this.getOptional(factoryName, feignContext, SetterFactory.class);

  7.    if (setterFactoryBean != null) {

  8.        this.setterFactory(setterFactoryBean);

  9.    }


  10.    // 以下为获取降级策略代码,构建降级,这里去掉了降级非空的非空的校验

  11.    Class<?> fallback = feignClient.fallback();

  12.    if (fallback != void.class) {

  13.        return targetWithFallback(factoryName, feignContext, target, this, fallback);

  14.    }

  15.    Class<?> fallbackFactory = feignClient.fallbackFactory();

  16.    if (fallbackFactory != void.class) {

  17.        return targetWithFallbackFactory(factoryName, feignContext, target, this, fallbackFactory);

  18.    }

  19.    return build().newInstance(target);

  20. }

3. 构建feign 客户端执行PigxHystrixInvocationHandler的增强

  1. Feign build(@Nullable final FallbackFactory<?> nullableFallbackFactory) {

  2.        super.invocationHandlerFactory((target, dispatch) ->

  3.                new PigxHystrixInvocationHandler(target, dispatch, setterFactory, nullableFallbackFactory));

  4.        super.contract(new HystrixDelegatingContract(contract));

  5.        return super.build();

  6.    }

4. PigxHystrixInvocationHandler.getFallback() 获取降级策略

  1.    @Override

  2.    @Nullable

  3.    @SuppressWarnings("unchecked")

  4.    protected Object getFallback() {

  5.            // 如果 @FeignClient  没有配置降级策略,使用动态代理创建一个

  6.            if (fallbackFactory == null) {

  7.                fallback = PigxFeignFallbackFactory.INSTANCE.create(target.type(), getExecutionException());

  8.            } else {

  9.              // 如果 @FeignClient配置降级策略,使用配置的

  10.                fallback = fallbackFactory.create(getExecutionException());

  11.            }

  12.    }

5. PigxFeignFallbackFactory.create 动态代理逻辑

  1.    public T create(final Class<?> type, final Throwable cause) {

  2.        return (T) FALLBACK_MAP.computeIfAbsent(type, key -> {

  3.            Enhancer enhancer = new Enhancer();

  4.            enhancer.setSuperclass(key);

  5.            enhancer.setCallback(new PigxFeignFallbackMethod(type, cause));

  6.            return enhancer.create();

  7.        });

  8.    }

6. PigxFeignFallbackMethod.intercept, 默认的降级逻辑,输出降级方法信息和错误信息,并且把错误格式

  1. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) {

  2.    log.error("Fallback class:[{}] method:[{}] message:[{}]",

  3.            type.getName(), method.getName(), cause.getMessage());


  4.    if (R.class == method.getReturnType()) {

  5.        final R result = cause instanceof PigxFeignException ?

  6.                ((PigxFeignException) cause).getResult() : R.builder()

  7.                .code(CommonConstants.FAIL)

  8.                .msg(cause.getMessage()).build();

  9.        return result;

  10.    }

  11.    return null;

  12. }

   

关注我们

  • Spring Cloud 微服务开发核心包mica

  • 基于Spring Cloud、OAuth2.0开发基于Vue前后分离的开发平台


推荐: Spring Cloud Alibaba基础教程:使用Nacos实现服务注册与发现

上一篇:Nacos配置的加载规则详解



 关注公众号

点击原文阅读更多


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存