Spring Cloud 负载平衡器架构选型

打印 上一主题 下一主题

主题 980|帖子 980|积分 2940

优质博文:IT-BLOG-CN
我们这次项目主要从RestTemplate 和 Feign 进行选型分析。
一、Spring Cloud Feign分析

   Feign是另外一种客户端负载平衡实现。
  我在该模块写了Feign Client的示例代码。
【1】spring-cloud-web-demo-api为服务的sdk模块
【2】spring-cloud-web-demo-service为提供接口服务的模块
【3】spring-cloud-web-demo-client为模拟调用服务的模块
首先在spring-cloud-web-demo-api模块,定义Feign API。spring-cloud-web-demo为spring-cloud-web-demo-service暴露的服务名。
  1. @FeignClient(value = "spring-cloud-web-demo")
  2. public interface UserFeign {
  3.     @GetMapping(value = "/user/getUserById", produces = "application/json;charset=utf-8")
  4.     Object getUserById(@RequestParam(value = "id", required = false) Long id);
  5.     //省略
  6. }
复制代码
然后通过ClientAutoConfiguration主动装配。(client直接引入api包就可以使用,不需要再EnableFeignClients)
  1. @Configuration
  2. @EnableFeignClients("net.teaho.demo.spring.cloud.web.api")
  3. public class ClientAutoConfiguration {
  4. }
  5. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  6. net.teaho.demo.spring.cloud.web.api.config.ClientAutoConfiguration
复制代码
在service模块如以往Spring MVC般实现api模块接口即可。
  1. @RestController
  2. public class UserController implements UserFeign {
  3.     private static final Logger logger = LoggerFactory.getLogger(UserController.class);
  4.     @Override
  5.     public Object getUserById(Long id) {
  6.         return "{"id":1, "name": "test"}";
  7.     }
  8.     //省略
  9. }
复制代码
在Client模块,注入bean后直接调用。
  1. @Component
  2. @Slf4j
  3. public class TestService {
  4.     @Autowired
  5.     private RestTemplate restTemplate;
  6.     public Object getOneUser(){
  7.         return userController.getUserById(1L);
  8.     }
  9. }
复制代码
二、RestTemplate分析

写了具有客户端负载平衡能力的RestTemplate的哀求代码。
类似这样定义:
  1. @Bean
  2. @LoadBalanced
  3. public RestTemplate restTemplate() {
  4.     return new RestTemplate();
  5. }
复制代码
RestTemplate毕竟是如何使用注册中央实现客户端负载平衡的呢?
实现方式: 就是将上面所说的LoadBalancerInterceptor负载平衡拦截器加到标注了@LoadBalanced的RestTemplate实例中。 LoadBalancerInterceptor拦截器会在执行过程中获取并设置适合的目的哀求实例,重新构造哀求URI。
  1. // 将配置中标注了@LoadBalanced的RestTemplate注入到这里
  2. @LoadBalanced
  3. @Autowired(required = false)
  4. private List<RestTemplate> restTemplates = Collections.emptyList();
  5. //将注册的RestTemplateCustomizer(RestTemplate自定义器)集合处理上面的restTemplates集合
  6. @Bean
  7. public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
  8.         final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
  9.     return () -> restTemplateCustomizers.ifAvailable(customizers -> {
  10.         for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
  11.             for (RestTemplateCustomizer customizer : customizers) {
  12.                 customizer.customize(restTemplate);
  13.             }
  14.         }
  15.     });
  16. }
复制代码
三、技能选型

最终选择使用OpenFeign,下面说说缘故原由。
和RestTemplate比起来,OpenFeign显得更适合Spring Boot微服务。
Open Feign相当于(HTTP)RPC,相比起RestTemplate,它直接显式将API声明以JAVA接口形式标识出来。 并且因为底层用的动态署理,它还可以(无感知地)更换底层实现。好比,github上就有更换底层逻辑的repo – Open Feign+Dubbo的RPC实现。
通过sdk包的形式,方便了调用,不需要像RestTemplate一样,客户端自行拼接上一串哀求参数。在代码编写上也清晰。
要使用就必须知道OpenFeign是怎么实现的呢?
四、OpenFeign 初始化分析

流程图如下:

看看前面例子里我们引入的OpenFeign的东西
【1】@EnableFeignClients(“net.teaho.demo.spring.cloud.web.api”)
【2】@FeignClient(value = “spring-cloud-web-demo”) 还有主动装配引入的
【3】FeignRibbonClientAutoConfiguration
【4】FeignClientsConfiguration
我们就从这两个注解开始分析源码。
【1】首先看@FeignClient注解。
  1. //给接口标注成一个REST调用方
  2. @Target(ElementType.TYPE)
  3. @Retention(RetentionPolicy.RUNTIME)
  4. @Documented
  5. public @interface FeignClient {
  6.     //服务名,可以带协议前缀,也可以用${property.name}关联一个配置值。
  7.     @AliasFor("name")
  8.     String value() default "";
  9.     @Deprecated
  10.     String serviceId() default "";
  11.     //bean name
  12.     String contextId() default "";
  13.     @AliasFor("value")
  14.     String name() default "";
  15.     /**
  16.      * Sets the <code>@Qualifier</code> value for the feign client.
  17.      */
  18.     String qualifier() default "";
  19.     //直接指定一个地址,比如http://localhost:12345,一般用于调试
  20.     String url() default "";
  21.     boolean decode404() default false;
  22.     /**
  23.      * A custom <code>@Configuration</code> for the feign client. Can contain override
  24.      * <code>@Bean</code> definition for the pieces that make up the client, for instance
  25.      * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
  26.      *
  27.      * @see FeignClientsConfiguration for the defaults
  28.      */
  29.     //可用于覆盖FeignClient默认设置
  30.     Class<?>[] configuration() default {};
  31.     //回滚类,像我的例子中定义的回滚类必须实现UserFeign接口,看https://cloud.spring.io/spring-cloud-static/Greenwich.SR5/single/spring-cloud.html#spring-cloud-feign-hystrix-fallback
  32.     Class<?> fallback() default void.class;
  33.     //如果需要对异常做诊断可用此属性,https://cloud.spring.io/spring-cloud-static/Greenwich.SR5/single/spring-cloud.html#spring-cloud-feign-hystrix-fallback
  34.     Class<?> fallbackFactory() default void.class;
  35.     //路径前缀
  36.     String path() default "";
  37.     //标记bean是否为primary
  38.     boolean primary() default true;
  39. }
复制代码
【2】接下来重点关注@EnableFeignClients注解是如何扫描FeignClient接口的。
  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target(ElementType.TYPE)
  3. @Documented
  4. @Import(FeignClientsRegistrar.class)
  5. public @interface EnableFeignClients {
  6.     //省略
  7. }
复制代码
嗯,发现没有,就是FeignClientsRegistrar做处理惩罚的。来分析下重点方法registerFeignClients和registerFeignClient
  1. class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
  2.         ResourceLoaderAware, EnvironmentAware {
  3.     public void registerFeignClients(AnnotationMetadata metadata,
  4.             BeanDefinitionRegistry registry) {
  5.         //classPath扫描器
  6.         ClassPathScanningCandidateComponentProvider scanner = getScanner();
  7.         scanner.setResourceLoader(this.resourceLoader);
  8.         //ClassPathScanningCandidateComponentProvider扫描的basePackage集合
  9.         Set<String> basePackages;
  10.         Map<String, Object> attrs = metadata
  11.                 .getAnnotationAttributes(EnableFeignClients.class.getName());
  12.         //扫描器用于扫描标注了@FeignClient类的拦截器
  13.         AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
  14.                 FeignClient.class);
  15.         final Class<?>[] clients = attrs == null ? null
  16.                 : (Class<?>[]) attrs.get("clients");
  17.         //clients属性为空,以@EnableFeignClients的value、basePackage等为根包扫描
  18.         if (clients == null || clients.length == 0) {
  19.             scanner.addIncludeFilter(annotationTypeFilter);
  20.             basePackages = getBasePackages(metadata);
  21.         }
  22.         //@EnableFeignClients的clients属性不为空,解析clients的类和根包
  23.         else {
  24.             final Set<String> clientClasses = new HashSet<>();
  25.             basePackages = new HashSet<>();
  26.             for (Class<?> clazz : clients) {
  27.                 basePackages.add(ClassUtils.getPackageName(clazz));
  28.                 clientClasses.add(clazz.getCanonicalName());
  29.             }
  30.             AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
  31.                 @Override
  32.                 protected boolean match(ClassMetadata metadata) {
  33.                     String cleaned = metadata.getClassName().replaceAll("\\$", ".");
  34.                     return clientClasses.contains(cleaned);
  35.                 }
  36.             };
  37.             scanner.addIncludeFilter(
  38.                     new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
  39.         }
  40.         //1.根据basePackage找到目标@FeignClient接口
  41.         //2.检查是否为接口
  42.         //3.将找到的接口注册为FeignClientFactoryBean
  43.         for (String basePackage : basePackages) {
  44.             Set<BeanDefinition> candidateComponents = scanner
  45.                     .findCandidateComponents(basePackage);
  46.             for (BeanDefinition candidateComponent : candidateComponents) {
  47.                 if (candidateComponent instanceof AnnotatedBeanDefinition) {
  48.                     // verify annotated class is an interface
  49.                     AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
  50.                     AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
  51.                     Assert.isTrue(annotationMetadata.isInterface(),
  52.                             "@FeignClient can only be specified on an interface");
  53.                     Map<String, Object> attributes = annotationMetadata
  54.                             .getAnnotationAttributes(
  55.                                     FeignClient.class.getCanonicalName());
  56.                     String name = getClientName(attributes);
  57.                     registerClientConfiguration(registry, name,
  58.                             attributes.get("configuration"));
  59.                     registerFeignClient(registry, annotationMetadata, attributes);
  60.                 }
  61.             }
  62.         }
  63.     }
  64.     private String getClientName(Map<String, Object> client) {
  65.         if (client == null) {
  66.             return null;
  67.         }
  68.         String value = (String) client.get("contextId");
  69.         if (!StringUtils.hasText(value)) {
  70.             value = (String) client.get("value");
  71.         }
  72.         if (!StringUtils.hasText(value)) {
  73.             value = (String) client.get("name");
  74.         }
  75.         if (!StringUtils.hasText(value)) {
  76.             value = (String) client.get("serviceId");
  77.         }
  78.         if (StringUtils.hasText(value)) {
  79.             return value;
  80.         }
  81.         throw new IllegalStateException("Either 'name' or 'value' must be provided in @"
  82.                 + FeignClient.class.getSimpleName());
  83.     }
  84.     private void registerFeignClient(BeanDefinitionRegistry registry,
  85.             AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
  86.         String className = annotationMetadata.getClassName();
  87.         BeanDefinitionBuilder definition = BeanDefinitionBuilder
  88.                 .genericBeanDefinition(FeignClientFactoryBean.class);
  89.         validate(attributes);
  90.         definition.addPropertyValue("url", getUrl(attributes));
  91.         definition.addPropertyValue("path", getPath(attributes));
  92.         String name = getName(attributes);
  93.         definition.addPropertyValue("name", name);
  94.         String contextId = getContextId(attributes);
  95.         definition.addPropertyValue("contextId", contextId);
  96.         definition.addPropertyValue("type", className);
  97.         definition.addPropertyValue("decode404", attributes.get("decode404"));
  98.         definition.addPropertyValue("fallback", attributes.get("fallback"));
  99.         definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
  100.         definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
  101.         String alias = contextId + "FeignClient";
  102.         AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
  103.         boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null
  104.         beanDefinition.setPrimary(primary);
  105.         String qualifier = getQualifier(attributes);
  106.         if (StringUtils.hasText(qualifier)) {
  107.             alias = qualifier;
  108.         }
  109.         BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
  110.                 new String[] { alias });
  111.         BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
  112.     }
  113. }
复制代码
可以看到最后注册beanDefinition时,我们看到注册了FeignClientFactoryBean这一FactoryBean。 我们看看工厂bean FeignClientFactoryBean是如何构造对象的。
  1. class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
  2.         ApplicationContextAware {
  3. //省略
  4.     @Override
  5.     public Object getObject() throws Exception {
  6.         return getTarget();
  7.     }
  8.     <T> T getTarget() {
  9.         //1.获取FeignContext,在FeignAutoConfiguration声明
  10.         FeignContext context = applicationContext.getBean(FeignContext.class);
  11.         //2.构造Feign builder
  12.         Feign.Builder builder = feign(context);
  13.         //3.如果没有设置url参数
  14.         if (!StringUtils.hasText(this.url)) {
  15.             if (!this.name.startsWith("http")) {
  16.                 url = "http://" + this.name;
  17.             }
  18.             else {
  19.                 url = this.name;
  20.             }
  21.             //4.设置path
  22.             url += cleanPath();
  23.             //5.获取Client(用于执行最终HTTP/HTTPS请求,比如LoadBalancerFeignClient),
  24.             //构造反射实例
  25.             return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type,
  26.                     this.name, url));
  27.         }
  28.         //存在url参数,构造非loadBalance的请求实例(target)
  29.         if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
  30.             this.url = "http://" + this.url;
  31.         }
  32.         String url = this.url + cleanPath();
  33.         Client client = getOptional(context, Client.class);
  34.         if (client != null) {
  35.             if (client instanceof LoadBalancerFeignClient) {
  36.                 // not load balancing because we have a url,
  37.                 // but ribbon is on the classpath, so unwrap
  38.                 client = ((LoadBalancerFeignClient)client).getDelegate();
  39.             }
  40.             builder.client(client);
  41.         }
  42.         Targeter targeter = get(context, Targeter.class);
  43.         return (T) targeter.target(this, builder, context, new HardCodedTarget<>(
  44.                 this.type, this.name, url));
  45.     }
  46.     //在FeignContext中获取一些在FeignClientsConfiguration中声明,Feign需要用到的组件
  47.     protected Feign.Builder feign(FeignContext context) {
  48.         FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
  49.         Logger logger = loggerFactory.create(this.type);
  50.         // @formatter:off
  51.         Feign.Builder builder = get(context, Feign.Builder.class)
  52.                 // required values
  53.                 .logger(logger)
  54.                 .encoder(get(context, Encoder.class))
  55.                 .decoder(get(context, Decoder.class))
  56.                 .contract(get(context, Contract.class));
  57.         // @formatter:on
  58.         configureFeign(context, builder);
  59.         return builder;
  60.     }
  61.     protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
  62.             HardCodedTarget<T> target) {
  63.         //获取Client
  64.         Client client = getOptional(context, Client.class);
  65.         if (client != null) {
  66.             builder.client(client);
  67.             //从Context获取Targeter,Targeter用于生成最终target实例(对应我的例子是被调用的通过反射生成的UserFeign实例)
  68.             Targeter targeter = get(context, Targeter.class);
  69.             return targeter.target(this, builder, context, target);
  70.         }
  71.         throw new IllegalStateException(
  72.                 "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
  73.     }
  74. //省略
  75. }
复制代码
在非调试环境下(即我们没设置url参数), 我们来看看targeter.target(this, builder, context, target)做了什么。
Targeter接口是构造被哀求的署理bean的类。有两个实现类HystrixTargeter、DefaultTargeter。
HystrixTargeter会比默认的多设置一些回滚措施,用到Feign的Contract属性, 我会先从DefaultTargeter说起。
DefaultTargeter会通过Feign.Builder#target(Target target)生成实例。我们来看看代码。
  1. public abstract class Feign {
  2. //省略
  3.   public static class Builder {
  4.     private final List<RequestInterceptor> requestInterceptors =
  5.         new ArrayList<RequestInterceptor>();
  6.     private Logger.Level logLevel = Logger.Level.NONE;
  7.     private Contract contract = new Contract.Default();
  8.     private Client client = new Client.Default(null, null);
  9.     private Retryer retryer = new Retryer.Default();
  10.     private Logger logger = new NoOpLogger();
  11.     private Encoder encoder = new Encoder.Default();
  12.     private Decoder decoder = new Decoder.Default();
  13.     private QueryMapEncoder queryMapEncoder = new QueryMapEncoder.Default();
  14.     private ErrorDecoder errorDecoder = new ErrorDecoder.Default();
  15.     private Options options = new Options();
  16.     private InvocationHandlerFactory invocationHandlerFactory =
  17.         new InvocationHandlerFactory.Default();
  18.     private boolean decode404;
  19.     private boolean closeAfterDecode = true;
  20.     private ExceptionPropagationPolicy propagationPolicy = NONE;
  21.     //省略
  22.     public <T> T target(Class<T> apiType, String url) {
  23.       return target(new HardCodedTarget<T>(apiType, url));
  24.     }
  25.     public <T> T target(Target<T> target) {
  26.       return build().newInstance(target);
  27.     }
  28.     //默认实现就是创建一个ReflectiveFeign实例
  29.     public Feign build() {
  30.       SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
  31.           new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
  32.               logLevel, decode404, closeAfterDecode, propagationPolicy);
  33.       ParseHandlersByName handlersByName =
  34.           new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
  35.               errorDecoder, synchronousMethodHandlerFactory);
  36.       return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
  37.     }
  38.   }
  39. //省略
  40. }
复制代码
在解读ReflectiveFeign前介绍几个概念:
1、InvocationHandlerFactory 是控制反射方法分发的接口,create方法返回InvocationHandler。
2、InvocationHandlerFactory.MethodHandler 最终将对署理类方法调用转换成HTTP哀求的地方,请看实现类SynchronousMethodHandler
3、InvocationHandlerFactory.Default 默认实现,作为构造参数传入ReflectiveFeign,create方法创建的是new ReflectiveFeign.FeignInvocationHandler(target, dispatch)。
4、ReflectiveFeign.ParseHandlersByName 作为构造参数传入ReflectiveFeign,核心方法apply(Target key)先将标注了@FeignClient的接口的方法解析出待处理惩罚的元数据List, 然后创建出方法名和方法处理惩罚器的map映射Map<String, MethodHandler>String是方法名,方法处理惩罚器通过SynchronousMethodHandler.Factory#create创建。
5、FeignInvocationHandler 为处理惩罚一样平常方法的处理惩罚器
6、DefaultMethodHandler 为处理惩罚接口默认方法的处理惩罚器
有了以上介绍,接下来简朴分析ReflectiveFeign的newInstance方法。
  1. public class ReflectiveFeign extends Feign {
  2.   private final ParseHandlersByName targetToHandlersByName;
  3.   private final InvocationHandlerFactory factory;
  4.   private final QueryMapEncoder queryMapEncoder;
  5.   ReflectiveFeign(ParseHandlersByName targetToHandlersByName, InvocationHandlerFactory factory,
  6.       QueryMapEncoder queryMapEncoder) {
  7.     this.targetToHandlersByName = targetToHandlersByName;
  8.     this.factory = factory;
  9.     this.queryMapEncoder = queryMapEncoder;
  10.   }
  11.   ..
  12.   /**
  13.    * creates an api binding to the {@code target}. As this invokes reflection, care should be taken
  14.    * to cache the result.
  15.    */
  16.   @SuppressWarnings("unchecked")
  17.   @Override
  18.   public <T> T newInstance(Target<T> target) {
  19.     //创建方法名和方法处理器的map映射
  20.     Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
  21.     Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
  22.     List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
  23.     for (Method method : target.type().getMethods()) {
  24.       if (method.getDeclaringClass() == Object.class) {
  25.         continue;
  26.       //判断是否为接口的默认方法,DefaultMethodHandler的处理逻辑是直接调用会原接口的default方法
  27.       } else if (Util.isDefault(method)) {
  28.         DefaultMethodHandler handler = new DefaultMethodHandler(method);
  29.         defaultMethodHandlers.add(handler);
  30.         methodToHandler.put(method, handler);
  31.       } else {
  32.         //方法处理map
  33.         methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
  34.       }
  35.     }
  36.     InvocationHandler handler = factory.create(target, methodToHandler);
  37.     //jdk动态代理创建对象
  38.     T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
  39.         new Class<?>[] {target.type()}, handler);
  40.     //将默认方法处理器也绑定到代理对象上
  41.     for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
  42.       defaultMethodHandler.bindTo(proxy);
  43.     }
  44.     return proxy;
  45.   }
  46.   static class FeignInvocationHandler implements InvocationHandler {
  47.     private final Target target;
  48.     private final Map<Method, MethodHandler> dispatch;
  49.     //省略
  50.     @Override
  51.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  52.       //自定义的equals、hashCode和toString的处理
  53.       if ("equals".equals(method.getName())) {
  54.         try {
  55.           Object otherHandler =
  56.               args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
  57.           return equals(otherHandler);
  58.         } catch (IllegalArgumentException e) {
  59.           return false;
  60.         }
  61.       } else if ("hashCode".equals(method.getName())) {
  62.         return hashCode();
  63.       } else if ("toString".equals(method.getName())) {
  64.         return toString();
  65.       }
  66.       //分发调用到对应方法的InvocationHandlerFactory.MethodHandler
  67.       return dispatch.get(method).invoke(args);
  68.     }
  69.     //省略
  70. }
复制代码
初始化完成。
五、OpenFeign 执行分析


上图是OpenFeign构造的署理对象被调用时的时序图。
1、署理对象被执行
2、找到对应SynchronousMethodHandler进行方法调用。
3、构造RequestTemplate
4、LoadBalancerFeignClient执行负载哀求
5、FeignLoadBalancer通过ILoadBalancer选择合适Server,通过Server重组URI,通过RibbonRequest持有的Client执行实际HTTP哀求包装成Response。
6、SynchronousMethodHandler通过Decoder将哀求响应用Decoder解码成最终效果。
下面介绍执行过程中涉及到源码中的部分组件。
1、RequestTemplate 是一个HTTP哀求内容的抽象。
2、RequestTemplate.Factory 将方法参数解析成RequestTemplate。
3、Retryer 我在上面的时序图没有标注出来,实际上它在SynchronousMethodHandler的执行中控制重试逻辑。
4、RequestInterceptor 在SynchronousMethodHandler发起执行中,会使用该拦截器对RequestTemplate进行处理惩罚。这是一个拓展点。
5、Logger 执行哀求时打日记(在debug时打)。默认为Logger.Level.NONE即不打日记,可以增加bean覆盖。


  • Logger.Level.NONE 不打印信息
  • Logger.Level.BASIC 打印哀求url和响应码。
  • Logger.Level.HEADERS 打印BASIC信息外加header信息
  • Logger.Level.FULL 打印所有
6、LoadBalancerFeignClient Client接口的实现类,是具有负载平衡能力的Client。Client接口为执行HTTP的接口,Client.Default是最终发出HTTP哀求的类。

7、FeignLoadBalancer FeignLoadBalancer通过ILoadBalancer选择合适Server,通过Server重组URI,通过RibbonRequest持有的Client执行实际HTTP哀求包装成Response。
8、LoadBalancerCommand ribbon的rxJava实现,执行负载流程逻辑的组件。
9、ILoadBalancer ribbon的负载平衡器抽象。
熔断: 在FeignClientsConfiguration中, 当配置了feign.hystrix.enabled,Feign Builder使用HystrixFeign.builder()。
以是build的时候新建HystrixInvocationHandler和HystrixDelegatingContract实例。
  1. Feign build(final FallbackFactory<?> nullableFallbackFactory) {
  2.    super.invocationHandlerFactory(new InvocationHandlerFactory() {
  3.      @Override
  4.      public InvocationHandler create(Target target,
  5.                                      Map<Method, MethodHandler> dispatch) {
  6.        return new HystrixInvocationHandler(target, dispatch, setterFactory,
  7.            nullableFallbackFactory);
  8.      }
  9.    });
  10.    super.contract(new HystrixDelegatingContract(contract));
  11.    return super.build();
  12. }
复制代码
来看看HystrixInvocationHandler的hystrix调用代码
  1. final class HystrixInvocationHandler implements InvocationHandler {
  2.    //省略
  3.   @Override
  4.   public Object invoke(final Object proxy, final Method method, final Object[] args)
  5.       throws Throwable {
  6.    //省略
  7.     HystrixCommand<Object> hystrixCommand =
  8.         new HystrixCommand<Object>(setterMethodMap.get(method)) {
  9.           //实际执行
  10.           @Override
  11.           protected Object run() throws Exception {
  12.             try {
  13.               return HystrixInvocationHandler.this.dispatch.get(method).invoke(args);
  14.             } catch (Exception e) {
  15.               throw e;
  16.             } catch (Throwable t) {
  17.               throw (Error) t;
  18.             }
  19.           }
  20.           @Override
  21.           protected Object getFallback() {
  22.             if (fallbackFactory == null) {
  23.               return super.getFallback();
  24.             }
  25.             try {
  26.               //用配置的fallbackFactory创建fallback实例
  27.               Object fallback = fallbackFactory.create(getExecutionException());
  28.               Object result = fallbackMethodMap.get(method).invoke(fallback, args);
  29.               //根据fallback对象的returntype解析包装内的结果返回
  30.               if (isReturnsHystrixCommand(method)) {
  31.                 return ((HystrixCommand) result).execute();
  32.               } else if (isReturnsObservable(method)) {
  33.                 // Create a cold Observable
  34.                 return ((Observable) result).toBlocking().first();
  35.               } else if (isReturnsSingle(method)) {
  36.                 // Create a cold Observable as a Single
  37.                 return ((Single) result).toObservable().toBlocking().first();
  38.               } else if (isReturnsCompletable(method)) {
  39.                 ((Completable) result).await();
  40.                 return null;
  41.               } else {
  42.                 return result;
  43.               }
  44.             } catch (IllegalAccessException e) {
  45.               // shouldn't happen as method is public due to being an interface
  46.               throw new AssertionError(e);
  47.             } catch (InvocationTargetException e) {
  48.               // Exceptions on fallback are tossed by Hystrix
  49.               throw new AssertionError(e.getCause());
  50.             }
  51.           }
  52.         };
  53.     //根据方法的return去返回结果
  54.     if (Util.isDefault(method)) {
  55.       return hystrixCommand.execute();
  56.     } else if (isReturnsHystrixCommand(method)) {
  57.       return hystrixCommand;
  58.     } else if (isReturnsObservable(method)) {
  59.       // Create a cold Observable
  60.       return hystrixCommand.toObservable();
  61.     } else if (isReturnsSingle(method)) {
  62.       // Create a cold Observable as a Single
  63.       return hystrixCommand.toObservable().toSingle();
  64.     } else if (isReturnsCompletable(method)) {
  65.       return hystrixCommand.toObservable().toCompletable();
  66.     }
  67.     return hystrixCommand.execute();
  68.   }
  69.    //省略
  70. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

盛世宏图

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表