spring cloudSpring Cloud

Spring Cloud Feign 分析(六)之FeignCl

2021-04-03  本文已影响0人  Blog

前面我们总结了FeignClient代理对象的生成过程,这一节我们也直奔主题:FeignClient调用过程,了解FeignClient调用过程有利于我们提高分析与解决问题的能力!FeignClient的调用过程可以概括为两部分组成,前部分为Hystrix熔断,后部分为Ribbon负载均衡请求及响应返回值!


我们在Spring Cloud Feign 分析(五)之FeignClient代理生成过程中讲解过在生成代理对象的过程中,会给代理对象设置HystrixInvocationHandler动态代理方法,FeignClient接口触发的方法调用均会被这个动态代理方法拦截,所以我们分析下HystrixInvocationHandler这个动态代理方法做了什么事情?


HystrixInvocationHandler

final class HystrixInvocationHandler implements InvocationHandler {
  ......
  @Override
  public Object invoke(final Object proxy, final Method method, final Object[] args)
      throws Throwable {
    ......
    //创建Hystrix命令
    HystrixCommand<Object> hystrixCommand =
        new HystrixCommand<Object>(setterMethodMap.get(method)) {
          @Override
          protected Object run() throws Exception {
            try {
              //调用SynchronousMethodHandler#invoke(Object[] argv)方法
              return HystrixInvocationHandler.this.dispatch.get(method).invoke(args);
            } catch (Exception e) {
              throw e;
            } catch (Throwable t) {
              throw (Error) t;
            }
          }
          //熔断降级方法,默认new UnsupportedOperationException("No fallback available.");
          @Override
          protected Object getFallback() {......}
        };

    if (Util.isDefault(method)) {
      return hystrixCommand.execute();
    } else if (isReturnsHystrixCommand(method)) {
      return hystrixCommand;
    } else if (isReturnsObservable(method)) {
      // Create a cold Observable
      return hystrixCommand.toObservable();
    } else if (isReturnsSingle(method)) {
      // Create a cold Observable as a Single
      return hystrixCommand.toObservable().toSingle();
    } else if (isReturnsCompletable(method)) {
      return hystrixCommand.toObservable().toCompletable();
    } else if (isReturnsCompletableFuture(method)) {
      return new ObservableCompletableFuture<>(hystrixCommand);
    }
    //通过Hystrix触发调用,让整个方法调用具备熔断能力
    return hystrixCommand.execute();
  }
  ......
}

这个代理方法,我们一眼望穿秋水,原来是创建了HystrixCommand命令来进行目标方法的调用分发,这样就使得我们的整个方法调用具备了熔断能力,我们接着看看dispatch.get(method).invoke(args),这个分发器的定义是这样的Map<Method, MethodHandler> dispatch,方法及对应方法的Handler,看起来有点眼熟是吧,我们在分析五(代理生成过程)中ReflectiveFeign#newInstance()这个方法中通过targetToHandlersByName解析而来的,通过这个解析器我们即可得知这个MethodHandler对应SynchronousMethodHandler,所以我们接着分析SynchronousMethodHandler,看看这个同步方法Handler做了哪些事情


SynchronousMethodHandler

final class SynchronousMethodHandler implements MethodHandler {
  ......
  @Override
  public Object invoke(Object[] argv) throws Throwable {
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    Options options = findOptions(argv);
    Retryer retryer = this.retryer.clone();
    while (true) {
      try {
        //执行目标方法
        return executeAndDecode(template, options);
      } catch (RetryableException e) {
        ......
        continue;
      }
    }
  }

  Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    //创建目标Request
    //内部会调用我们自定义的RequestInterceptor拦截方法,比如添加traceId头信息用于跟踪等等
    Request request = targetRequest(template);
    if (logLevel != Logger.Level.NONE) {
      logger.logRequest(metadata.configKey(), logLevel, request);
    }
    Response response;
    long start = System.nanoTime();
    try {
      //执行LoadBalancerFeignClient#execute()
      response = client.execute(request, options);
      //确保请求参数已设置
      response = response.toBuilder()
          .request(request)
          .requestTemplate(template)
          .build();
    } catch (IOException e) {
      if (logLevel != Logger.Level.NONE) {
        logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
      }
      throw errorExecuting(request, e);
    }
    long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
    //如果decoder有则使用
    if (decoder != null)
      return decoder.decode(response, metadata.returnType());
    //异步响应处理器对结果response做最后的解析
    CompletableFuture<Object> resultFuture = new CompletableFuture<>();
    asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,
        metadata.returnType(),
        elapsedTime);
    try {
      if (!resultFuture.isDone())
        throw new IllegalStateException("Response handling not done");
      //阻塞获取最终返回值
      return resultFuture.join();
    } catch (CompletionException e) {
      Throwable cause = e.getCause();
      if (cause != null)
        throw cause;
      throw e;
    }
  }
  ......
}

在SynchronousMethodHandler这个同步方法处理器中我们看到最终调用了这么一段response = client.execute(request, options);这个client从上下文方法传递中也能看出对应LoadBalancerFeignClient这个负载均衡client,至此我们前半段部分Hystrix熔断分析完毕,下文部分将接着讲解后半段部分Ribbon负载均衡请求相关的调用过程


FeignRibbonClientAutoConfiguration

@ConditionalOnClass({ ILoadBalancer.class, Feign.class })
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.ribbon.enabled",matchIfMissing = true)
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(FeignAutoConfiguration.class)
@EnableConfigurationProperties({ FeignHttpClientProperties.class })
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
        OkHttpFeignLoadBalancedConfiguration.class,
        DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {
    //无RetryTemplate则使用此FeignLoadBalancer实例缓存工厂
    @Bean
    @Primary
    @ConditionalOnMissingBean
    @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
    public CachingSpringLoadBalancerFactory cachingLBClientFactory(
            SpringClientFactory factory) {
        return new CachingSpringLoadBalancerFactory(factory);
    }
    //有RetryTemplate则使用此FeignLoadBalancer实例缓存工厂
    @Bean
    @Primary
    @ConditionalOnMissingBean
    @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
    public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory(
            SpringClientFactory factory, LoadBalancedRetryFactory retryFactory) {
        return new CachingSpringLoadBalancerFactory(factory, retryFactory);
    }

    //设置默认请求选项,从Ribbon的SpringClientFactory获取IClientConfig配置参数
    @Bean
    @ConditionalOnMissingBean
    public Request.Options feignRequestOptions() {
        return LoadBalancerFeignClient.DEFAULT_OPTIONS;
    }
}

当前配置类会在FeignAutoConfiguration之前进行,CachingSpringLoadBalancerFactory工厂类会根据clientName创建FeignLoadBalancer并缓存供LoadBalancerFeignClient后续直接使用


DefaultFeignLoadBalancedConfiguration

@Configuration(proxyBeanMethods = false)
class DefaultFeignLoadBalancedConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
            SpringClientFactory clientFactory) {
        return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory,
                clientFactory);
    }
}

创建一个默认的Client => LoadBalancerFeignClient,具备负载均衡功能的Client,是Feign负载均衡客户端的默认实现。


CachingSpringLoadBalancerFactory

public class CachingSpringLoadBalancerFactory {
    private volatile Map<String, FeignLoadBalancer> cache = new ConcurrentReferenceHashMap<>();
    ......
    public FeignLoadBalancer create(String clientName) {
        //缓存中获取FeignLoadBalancer
        FeignLoadBalancer client = this.cache.get(clientName);
        if (client != null) {
            return client;
        }
        //通过Ribbon的SpringClientFactory工厂类获取Ribbon客户端相关配置接口
        IClientConfig config = this.factory.getClientConfig(clientName);
        //通过Ribbon的SpringClientFactory工厂类获取负载均衡接口
        ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
        //通过Ribbon的SpringClientFactory工厂类获取安全端口接口
        ServerIntrospector serverIntrospector = this.factory.getInstance(clientName,
                ServerIntrospector.class);
        //创建FeignLoadBalancer
        client = this.loadBalancedRetryFactory != null
                ? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,
                        this.loadBalancedRetryFactory)
                : new FeignLoadBalancer(lb, config, serverIntrospector);
        //放入缓存
        this.cache.put(clientName, client);
        return client;
    }
}

通过Ribbon对外提供的SpringClientFactory这个工厂类,我们可以获取各种Ribbon客户端相关的配置,Ribbon客户端配置详情介绍请参阅Spring Cloud Ribbon 分析(三)之RibbonClientConfiguration客户端配置


LoadBalancerFeignClient

public class LoadBalancerFeignClient implements Client {
    ......
    @Override
    public Response execute(Request request, Request.Options options) throws IOException {
        try {
            //转换为URI格式
            URI asUri = URI.create(request.url());
            //获取clientName
            String clientName = asUri.getHost();
            URI uriWithoutHost = cleanUrl(request.url(), clientName);
            //创建RibbonRequest
            FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
                    this.delegate, request, uriWithoutHost);
            //获取Ribbon IClientConfig
            IClientConfig requestConfig = getClientConfig(options, clientName);
            //创建FeignLoadBalancer并执行负载均衡方法
            return lbClient(clientName)
                    .executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
        }
        catch (ClientException e) {
            IOException io = findIOException(e);
            if (io != null) {
                throw io;
            }
            throw new RuntimeException(e);
        }
    }
    ......
    //如果有LoadBalancedRetryFactory则创建RetryableFeignLoadBalancer
    private FeignLoadBalancer lbClient(String clientName) {
        return this.lbClientFactory.create(clientName);
    }
    ......
}

上文中lbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse()执行过程可以参阅Spring Cloud Ribbon 分析(四)之Feign集成中的LoadBalancerFeignClient标题部分,其中会分析通过Ribbon发起负载均衡到获取返回值的过程,这里我们就不做重复阐述。


至此,Feign模块总结也告一段落,这里笔者只总结和分析了流程中比较重要的几个部分,如果想知道更多Feign原理则需要亲自分析相关的知识点,如果文章对你有所帮助,就点赞关注吧!

上一篇下一篇

猜你喜欢

热点阅读