美文网首页SpringCloud
HystrixFeign的详细构建过程

HystrixFeign的详细构建过程

作者: 逸飞兮 | 来源:发表于2019-05-23 21:27 被阅读63次

    spring-cloud-openfeign-core-2.1.1.RELEASE.jarHystrixFeign 的详细构建过程

    1. 准备工作(配置)

    1. FeignAutoConfiguration自动配置类
        @Configuration
        @ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
        protected static class HystrixFeignTargeterConfiguration {
    
            @Bean
            @ConditionalOnMissingBean
            public Targeter feignTargeter() {
                return new HystrixTargeter();
            }
    
        }
    
        @Configuration
        @ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
        protected static class DefaultFeignTargeterConfiguration {
    
            @Bean
            @ConditionalOnMissingBean
            public Targeter feignTargeter() {
                return new DefaultTargeter();
            }
    
        }
    
    1. feign.hystrix.HystrixFeign类存在时,将 HystrixTargeter 注册为 Targeter 类型的 bean
    2. feign.hystrix.HystrixFeign类不存在时,使用 DefaultTargeter
    3. 看起来似乎可以使用自定义的Targeter代替Hystrix或默认的,这样就可以自定义各种功能了。实际上不行,因为 Targeterpackage 访问级别的。
    1. FeignClientsConfiguration
    @Configuration
    public class FeignClientsConfiguration {
        
                
        @Bean
        @ConditionalOnMissingBean
        public Retryer feignRetryer() {
            return Retryer.NEVER_RETRY;
        }
    
        @Bean
        @Scope("prototype")
        @ConditionalOnMissingBean
        public Feign.Builder feignBuilder(Retryer retryer) {
            return Feign.builder().retryer(retryer);
        }
        
        @Configuration
        @ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
        protected static class HystrixFeignConfiguration {
            @Bean
            @Scope("prototype")
            @ConditionalOnMissingBean
            @ConditionalOnProperty(name = "feign.hystrix.enabled")
            public Feign.Builder feignHystrixBuilder() {
                return HystrixFeign.builder();
            }
        }
    }
    

    重要Feign 以及内部类 Feign.Builder 都是 public 访问级别,可以注入自定义的bean。

    2.EnableFeignClients与FeignClientsRegistrar类

    将使用@FeignClient注解的类注册成spring bean,并使用注解中的配置

    1. 在@EnableFeignClients注解中导入FeignClientsRegistrar类
    2. FeignClientsRegistrar类实现了ImportBeanDefinitionRegistrar类,会由spring框架执行实现方法 registerBeanDefinitions(AnnotationMetaData, BeanDefinitionRegistry)
    3. FeignClientsRegistrar中的 registerBeanDefinitions方法调用两个方法
      1. registerDefaultConfiguration:注册默认的配置
      2. registerFeignClients:注册Feign客户端(重点
    4. registerFeignClients:获取 @EnableFeignClients注解中定义的配置扫描feign客户端
    5. registerFeignClients:通过registerFeignClient(BeanDefinitionRegistry, AnnotationMetadata, Map)方法注册每一个feignClient,过程:先获取 @FeignClient注解中定义的配置,将配置应用在spring bean 工厂 FeignClientFactoryBean, 通过工厂类 FeignClientFactoryBean 为每一个使用@FeignClient注解的类生产 FeignClient,详细过程见下一节

    3.FeignClientFactoryBean

    FeignClient工厂bean。

    class FeignClientFactoryBean
        implements FactoryBean<Object>, InitializingBean, ApplicationContextAware{
        //...
    }
    

    通过实现方法 FactoryBean#getObject()来由spring框架生产FeignClient。

    @Override
    public Object getObject() throws Exception {
        return getTarget();
    }
    
    /**
     * 获得目标
     * 1. 获得FeignContext
     * 2. 从FeignContext中获得Feign构建器Feign.Builder
     * 3. 从FeignContext中获得Client,判断是否进行负载均衡
     * 4. 从FeignContext中获得Target,并执行Target的默认方法target(FeignClientFactoryBean, Feign.Builder,
                FeignContext, Target.HardCodedTarget<T>);
     * 5.由于一开始注入的Feign.Builder是HystrixFeign.Builder,则此处是调用HystrixFeign.Builder里的对应方法
     */
    <T> T getTarget() {
        FeignContext context = this.applicationContext.getBean(FeignContext.class);
        Feign.Builder builder = feign(context);
        //省略部分代码
        // ......
        Client client = getOptional(context, Client.class);
        if (client != null) {
            if (client instanceof LoadBalancerFeignClient) {
                // not load balancing because we have a url,
                // but ribbon is on the classpath, so unwrap
                client = ((LoadBalancerFeignClient) client).getDelegate();
            }
            builder.client(client);
        }
        Targeter targeter = get(context, Targeter.class);
        return (T) targeter.target(this, builder, context,
            new HardCodedTarget<>(this.type, this.name, url));
    }
    
        protected Feign.Builder feign(FeignContext context) {
            FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
            Logger logger = loggerFactory.create(this.type);
    
            // @formatter:off
            Feign.Builder builder = get(context, Feign.Builder.class)
                    // required values
                    .logger(logger)
                    .encoder(get(context, Encoder.class))
                    .decoder(get(context, Decoder.class))
                    .contract(get(context, Contract.class));
            // @formatter:on
    
            configureFeign(context, builder);
    
            return builder;
        }
    
    

    工厂获得对象(目标):

    1. 获得FeignContext(feign上下文)
    2. 从FeignContext中获得Feign构建器Feign.Builder(public,可以在此使用自定义构建器)
    3. 从FeignContext中获得Client,判断是否进行负载均衡
    4. 从FeignContext中获得Target,并执行Target的默认方法target(FeignClientFactoryBean, Feign.Builder,
      FeignContext, Target.HardCodedTarget<T>);
    5. 由于一开始注入的 *Targeter* 是 *HystrixTargeter* ,则此处是调用 HystrixTargeter 里的对应方法(从第一节的配置来看,只要 *feign.hystrix.HystrixFeign* 类存在,就是注入的 *HystrixTargeter *, 否则是 *DefaultTargeter*,对于需要**自定义构建feign的,这里不太重要**)
    

    4.Targeter

    4.1.HystrixTargeter

    class HystrixTargeter implements Targeter {
    
        @Override
        public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
                FeignContext context, Target.HardCodedTarget<T> target) {
            // 若不是 HystrixFeign,则执行其对应的默认target方法。
            // 此处只处理HystrixFeign。
            if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
                return feign.target(target);
            }
            feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
            SetterFactory setterFactory = getOptional(factory.getName(), context,
                    SetterFactory.class);
            if (setterFactory != null) {
                builder.setterFactory(setterFactory);
            }
            Class<?> fallback = factory.getFallback();
            if (fallback != void.class) {
                return targetWithFallback(factory.getName(), context, target, builder,
                        fallback);
            }
            Class<?> fallbackFactory = factory.getFallbackFactory();
            if (fallbackFactory != void.class) {
                return targetWithFallbackFactory(factory.getName(), context, target, builder,
                        fallbackFactory);
            }
    
            // 调用从Feign.Builder继承的方法。
            return feign.target(target);
        }
        
            private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
                Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
                Class<?> fallbackFactoryClass) {
            FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>) getFromContext(
                    "fallbackFactory", feignClientName, context, fallbackFactoryClass,
                    FallbackFactory.class);
            return builder.target(target, fallbackFactory);
        }
    
        private <T> T targetWithFallback(String feignClientName, FeignContext context,
                Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
                Class<?> fallback) {
            T fallbackInstance = getFromContext("fallback", feignClientName, context,
                    fallback, target.type());
            return builder.target(target, fallbackInstance);
        }
        
        //...
    }
    
    1. HystrixTarget只处理 Feign.Builder 类型为 feign.hystrix.HystrixFeign.Builder
    2. 若feign构建器不是 feign.hystrix.HystrixFeign.Builder 类型,则执行注入的 feign 构建器的默认target方法
    3. 因此,即使注入的 Targeter 是 HystrixTargeter,此处也可以执行自定义 Feign.Builder
    4. 理解:Feign.Builder#target(Target) 方法通常不会被 override(后续会讲解为什么不重写此方法)

    4.2.DefaultTargeter

    class DefaultTargeter implements Targeter {
        @Override
        public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
                FeignContext context, Target.HardCodedTarget<T> target) {
            return feign.target(target);
        }
    }
    
    1. 执行 Feign.Builder (子)类型对应的 默认 target方法。
    2. 理解:Feign.Builder#target(Target) 方法通常不会被 override(后续会讲解为什么不重写此方法)

    5.FeignBuilder

    feign构建器:构建feign对象。

    Feign的目的是将 http api 包装成 restful 风格以便开发。

    在实现中,Feign 是一个为目标http apis 生成 feign对象(Feign#newInstance)的工厂。

    上述步骤目前需要的都是通过对应的 Builder 构建对应的Feign。

    public abstract class Feign {
    
      public static Builder builder() {
        return new Builder();
      }
       
      public abstract <T> T newInstance(Target<T> target);
      
      public static class Builder {
        public <T> T target(Target<T> target) {
          return build().newInstance(target);
        }
    
        public Feign build() {
          SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
              new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger, logLevel, decode404, closeAfterDecode, propagationPolicy);
          ParseHandlersByName handlersByName =
              new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
                  errorDecoder, synchronousMethodHandlerFactory);
          return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
        }
      }
    }
    
    
    1. Feign.Builder#target(Target) 方法里面实际上调用的是 build() 方法来构建对象,因此重写 build() 方法即可,没有必要还重写 target(Target) 方法
    2. Feign 以及内部类 Feign.Builder 都是 public可以重写并注入自定义的bean

    5.1.HystrixFeign

    public final class HystrixFeign {
      public static final class Builder extends Feign.Builder {  
        @Override
        public Feign build() {
          return build(null);
        }
        
        // 提供一系列的target方法,支持各种配置:fallback、FallBackFactory等
        public <T> T target(Target<T> target, T fallback) {
          return build(fallback != null ? new FallbackFactory.Default<T>(fallback) : null)
              .newInstance(target);
        }
    
        public <T> T target(Target<T> target, FallbackFactory<? extends T> fallbackFactory) {
          return build(fallbackFactory).newInstance(target);
        }
          
        
        public <T> T target(Class<T> apiType, String url, T fallback) {
          return target(new Target.HardCodedTarget<T>(apiType, url), fallback);
        }
    
        public <T> T target(Class<T> apiType,
                            String url,
                            FallbackFactory<? extends T> fallbackFactory) {
          return target(new Target.HardCodedTarget<T>(apiType, url), fallbackFactory);
        }
    
        /** Configures components needed for hystrix integration. */
        Feign build(final FallbackFactory<?> nullableFallbackFactory) {
          super.invocationHandlerFactory(new InvocationHandlerFactory() {
            @Override
            public InvocationHandler create(Target target,
                                            Map<Method, MethodHandler> dispatch) {
              return new HystrixInvocationHandler(target, dispatch, setterFactory,
                  nullableFallbackFactory);
            }
          });
          super.contract(new HystrixDelegatingContract(contract));
          return super.build();
        }
    

    基本到了这一步,需要设置的东西,都可以配置了。

    1. 虽然 build 方法中涉及到 InvocationHandler,但基本不需要改什么,而 InvocationHandler 竟然也是 package 访问级别,所以只好复制一个,使用自己的。
    2. HystrixDelegatingContract 是 public 级别,不需要修改的话,仍然用这个。

    5.2示例

    以下示例参考 SentinelFeign

    @Override
    public Feign build() {
        super.invocationHandlerFactory(new InvocationHandlerFactory() {
            @Override
            public InvocationHandler create(Target target,
                                            Map<Method, MethodHandler> dispatch) {
                // using reflect get fallback and fallbackFactory properties from
                // FeignClientFactoryBean because FeignClientFactoryBean is a package
                // level class, we can not use it in our package
                Object feignClientFactoryBean = Builder.this.applicationContext
                    .getBean("&" + target.type().getName());
    
                Class fallback = (Class) getFieldValue(feignClientFactoryBean,
                                                       "fallback");
                Class fallbackFactory = (Class) getFieldValue(feignClientFactoryBean,
                                                              "fallbackFactory");
                String name = (String) getFieldValue(feignClientFactoryBean, "name");
    
                Object fallbackInstance;
                FallbackFactory fallbackFactoryInstance;
                // check fallback and fallbackFactory properties
                // 以下逻辑在HystrixTargeter中有,但执行自定义的builder,不会执行到那段逻辑,因此此处加上。
                if (void.class != fallback) {
                    fallbackInstance = getFromContext(name, "fallback", fallback,
                                                      target.type());
                    return new PegasusInvocationHandler(target, dispatch, setterFactory,
                                                        new FallbackFactory.Default(fallbackInstance));
                }
                if (void.class != fallbackFactory) {
                    fallbackFactoryInstance = (FallbackFactory) getFromContext(name,
                                                                               "fallbackFactory", fallbackFactory,
                                                                               FallbackFactory.class);
                    return new PegasusInvocationHandler(target, dispatch, setterFactory,
                                                        fallbackFactoryInstance);
                }
                // 此处还是会使用一个默认的FallbackFactory。
                return new PegasusInvocationHandler(target, dispatch, setterFactory, new PegasusFeignFallbackFactory<>(target));
            }
    
            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 HystrixDelegatingContract(contract));
        return super.build();
    }
    
    

    需要自定义fallbackFactory,则实现 feign.hystrix.FallbackFactory类,需要自定义fallback,则实现 org.springframework.cglib.proxy.MethodInterceptor即可

    6.总结

    1. 由于Feign构建过程所用到的 Targeterpackage 访问级别的,不能使用自定义的
    2. Feign以及Feign.Builderpublilc,给了我们扩展的空间。

    7.参考资料

    1. feign-hystrix-10.1.0.jarspring-cloud-openfeign-core-2.1.1.RELEASE.jar
    2. spring-cloud-alibaba-sentinel-0.9.0.RELEASE.jar中的 sentinelFeign 实现
    3. Spring Cloud Alibaba Sentinel 整合 Feign 的设计实现

    相关文章

      网友评论

        本文标题:HystrixFeign的详细构建过程

        本文链接:https://www.haomeiwen.com/subject/iidizqtx.html