美文网首页Spring Cloud
Spring Cloud Feign 分析(四)之FeignAu

Spring Cloud Feign 分析(四)之FeignAu

作者: Blog | 来源:发表于2021-03-22 22:49 被阅读0次

    前面几大章节我们分析和总结了Ribbon负载均衡、Hystrix熔断、Zuul网关这三大SpringBoot应用必不可少的利器,也在前面几节分析了Feign客户端的注册过程和Feign如何做到版本兼容相关的讲解,笔者想了想还是觉得少了点东西,觉得应该把Feign的整个调用链都进行总结一遍,后续我们扩展Feign时候才能游刃有余,所以本节将分析FeignAutoConfiguration入口配置,咱们分析的Feign版本为10.10.1版本,基于SpringBoot2.3.9.RELEASE!


    FeignAutoConfiguration

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(Feign.class)
    @EnableConfigurationProperties({ FeignClientProperties.class,
            FeignHttpClientProperties.class })
    @Import(DefaultGzipDecoderConfiguration.class)
    public class FeignAutoConfiguration {
        //@FeignClient注解生成的FeignClientSpecification对象
        @Autowired(required = false)
        private List<FeignClientSpecification> configurations = new ArrayList<>();
        //注册Feign特性描述
        @Bean
        public HasFeatures feignFeature() {
            return HasFeatures.namedFeature("Feign", Feign.class);
        }
        //创建Feign上下文
        @Bean
        public FeignContext feignContext() {
            FeignContext context = new FeignContext();
            context.setConfigurations(this.configurations);
            return context;
        }
        ......
        @Configuration(proxyBeanMethods = false)
        @ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
        protected static class HystrixFeignTargeterConfiguration {
            //注册具有Hystrix熔断功能的代理类
            @Bean
            @ConditionalOnMissingBean
            public Targeter feignTargeter() {
                return new HystrixTargeter();
            }
        }
        ......
    }
    

    在FeignAutoConfiguration这个入口配置类中我们只贴非常重要的片段,List<FeignClientSpecification> configurations这个@FeignClient注解生成的FeignClientSpecification对象可参阅Spring Cloud Feign 分析(一)之FeignClient注册过程,FeignContext与HystrixTargeter对象我们开始逐一讲解。


    FeignContext

    /**
    * 对外提供的实例工厂类,为每个Feign客户端创建一个ApplicationContext,并从中提取所需的Bean
    */
    public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
    
        public FeignContext() {
            super(FeignClientsConfiguration.class, "feign", "feign.client.name");
        }
        //获取实例
        @Nullable
        public <T> T getInstanceWithoutAncestors(String name, Class<T> type) {
            try {
                return BeanFactoryUtils.beanOfType(getContext(name), type);
            }
            catch (BeansException ex) {
                return null;
            }
        }
        //获取实例集合
        @Nullable
        public <T> Map<String, T> getInstancesWithoutAncestors(String name, Class<T> type) {
            return getContext(name).getBeansOfType(type);
        }
    }
    

    外部主要通过该工厂类获取FeignClient客户端实例,通过继承NamedContextFactory会为每一个FeignClient客户端都创建一个ApplicationContext,以便于从每个FeignClient的ApplicationContext获取指定的Bean对象,这个工厂类最主要的特征就是隔离了每个FeignClient,每个FeignClient客户端都有自己的一个ApplicationContext上下文。


    NamedContextFactory

    public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
            implements DisposableBean, ApplicationContextAware {
        ......
        //设置@FeignClient注解生成的FeignClientSpecification对象
        public void setConfigurations(List<C> configurations) {
            for (C client : configurations) {
                this.configurations.put(client.getName(), client);
            }
        }
        ......
        //获取@FeignClient对应的ApplicationContext 应用上下文
        protected AnnotationConfigApplicationContext getContext(String name) {
            if (!this.contexts.containsKey(name)) {
                synchronized (this.contexts) {
                    if (!this.contexts.containsKey(name)) {
                        //如果在contexts上下文Map中找到则创建一个@FeignClient对应的应用上下文
                        this.contexts.put(name, createContext(name));
                    }
                }
            }
            return this.contexts.get(name);
        }
        //创建FeignClient的ApplicationContext应用上下文
        protected AnnotationConfigApplicationContext createContext(String name) {
            //创建一个ApplicationContext
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
            if (this.configurations.containsKey(name)) {
                //如果有配置文件则注册到当前ApplicationContext应用上下文中
                //如果@FeignClients配置了configuration则将被注册到当前ApplicationContext应用上下文中
                for (Class<?> configuration : this.configurations.get(name)
                        .getConfiguration()) {
                    context.register(configuration);
                }
            }
            //@EnableFeignClients的defaultConfiguration配置将会被注册到ApplicationContext中
            for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
                if (entry.getKey().startsWith("default.")) {
                    for (Class<?> configuration : entry.getValue().getConfiguration()) {
                        context.register(configuration);
                    }
                }
            }
            //注册属性配置、FeignClientsConfiguration配置类
            context.register(PropertyPlaceholderAutoConfiguration.class,
                    this.defaultConfigType);
            //添加属性
            context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
                    this.propertySourceName,
                    Collections.<String, Object>singletonMap(this.propertyName, name)));
            //设置父类ApplicationContext,这样可以访问父类Bean
            if (this.parent != null) {
                context.setParent(this.parent);
                context.setClassLoader(this.parent.getClassLoader());
            }
            //设置DisplayName属性,格式为:FeignContext-FeignClient客户端name/value属性
            context.setDisplayName(generateDisplayName(name));
            context.refresh();
            return context;
        }
        //获取实例
        public <T> T getInstance(String name, Class<T> type) {
            AnnotationConfigApplicationContext context = getContext(name);
            try {
                return context.getBean(type);
            }
            catch (NoSuchBeanDefinitionException e) {
                // ignore
            }
            return null;
        }
        ......
    }
    

    通过NamedContextFactory中的代码片段以及注释信息,我们更佳直观的看出,和命名空间很像,每一个@FeignClient对应的FeignClientSpecification对象都会生成一个专属于这个@FeignClient的一个应用上下文ApplicationContext,起到了隔离作用,生成ApplicationContext应用上下文的步骤如下:

    1. 将@FeignClient注解生成的FeignClientSpecification对象添加到configurations配置Map中
    2. 根据@FeignClient的name、value属性生成一个ApplicationContext应用上下文
    3. 将@FeignClients中的configuration配置注册到当前ApplicationContext上下文中
    4. 将@EnableFeignClients的defaultConfiguration配置注册到当前上下文中
    5. 注册属性配置类、FeignClientsConfiguration客户端配置类
    6. 添加MapPropertySource属性配置
    7. 设置父类ApplicationContext,这样可以访问父类Bean
    8. 将当前创建的ApplicationContext添加到上下文contexts中,每个@FeignClient都会生成一个ApplicationContext上下文,起隔离的作用
    9. 对外提供各种getInstance获取实例方法

    上面我们分析了FeignContext这个上下文,为每个@FeignClient客户端创建一个ApplicationContext,外部通过这个工厂类获取所需要的实例Bean,那下面我们继续聊聊一个非常重要的目标执行类,HystrixTargeter具备了熔断能力的执行类!

    HystrixTargeter

    class HystrixTargeter implements Targeter {
    
        @Override
        public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
                FeignContext context, Target.HardCodedTarget<T> target) {
            //是否为Feign.Builder 类型,若不是则直接创建代理对象并执行
            if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
                return feign.target(target);
            }
            //转换为HystrixFeign.Builder类型
            feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
            //获取上下文id,其实就是获取的@FeignClient注解的name、value属性值
            String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName()
                    : factory.getContextId();
            //获取SetterFactory,主要是HystrixCommand的groupKey、commandKey参数,默认setterFactory为空
            SetterFactory setterFactory = getOptional(name, context, SetterFactory.class);
            //setterFactory不为空就设置
            if (setterFactory != null) {
                builder.setterFactory(setterFactory);
            }
            //获取降级方法,默认为void初始状态
            Class<?> fallback = factory.getFallback();
            if (fallback != void.class) {
                //如果有设置了fallback,则使用
                return targetWithFallback(name, context, target, builder, fallback);
            }
            //获取降级工厂类FallbackFactory,默认为void初始状态
            Class<?> fallbackFactory = factory.getFallbackFactory();
            if (fallbackFactory != void.class) {
                return targetWithFallbackFactory(name, context, target, builder,
                        fallbackFactory);
            }
            //调用HystrixFeign#build()
            return feign.target(target);
        }
        //具有FallbackFactory的目标执行类
        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);
            //返回具有FallbackFactory的代理实例
            return builder.target(target, fallbackFactory);
        }
        //具有Fallback的目标执行类
        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());
            //返回具有fallback的代理实例
            return builder.target(target, fallbackInstance);
        }
        //返回指定类型的实例
        private <T> T getFromContext(String fallbackMechanism, String feignClientName,
                FeignContext context, Class<?> beanType, Class<T> targetType) {
            Object fallbackInstance = context.getInstance(feignClientName, beanType);
            if (fallbackInstance == null) {
                throw new IllegalStateException(String.format(
                        "No " + fallbackMechanism
                                + " instance of type %s found for feign client %s",
                        beanType, feignClientName));
            }
    
            if (!targetType.isAssignableFrom(beanType)) {
                throw new IllegalStateException(String.format("Incompatible "
                        + fallbackMechanism
                        + " instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
                        beanType, targetType, feignClientName));
            }
            return (T) fallbackInstance;
        }
        //根据@FeignClient注解的name、value属性值获取对应beanType实例
        private <T> T getOptional(String feignClientName, FeignContext context,
                Class<T> beanType) {
            return context.getInstance(feignClientName, beanType);
        }
    }
    

    通过对HystrixTargeter的讲解,我们看到逻辑逐渐复杂起来,比较直观的能看出这个类具备了Hystrix熔断功能,再简单点,其实我们能这样理解:HystrixTargeter会返回一个具备Hystrix熔断功能的代理对象,内部执行顺序就是请求先经由Hystrix,然后再由LoadBalancerFeignClient进行负载均衡请求最终的结果!


    这里我们只是简单的总结了HystrixTargeter类的作用,feign.target(target)这一句之后的逻辑相对更佳复杂,及生成代理这个过程和代理内部又做了哪些事情,将会放在后续的文章中进行总结和讲解!

    相关文章

      网友评论

        本文标题:Spring Cloud Feign 分析(四)之FeignAu

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