前面我们总结了FeignClient代理对象的生成过程,这一节我们也直奔主题:FeignClient调用过程,了解FeignClient调用过程有利于我们提高分析与解决问题的能力!FeignClient的调用过程可以概括为两部分组成,前部分为Hystrix熔断,后部分为Ribbon负载均衡请求及响应返回值!
我们在Spring Cloud Feign 分析(五)之FeignClient代理生成过程中讲解过在生成代理对象的过程中,会给代理对象设置HystrixInvocationHandler动态代理方法,FeignClient接口触发的方法调用均会被这个动态代理方法拦截,所以我们分析下HystrixInvocationHandler这个动态代理方法做了什么事情?
final class HystrixInvocationHandler implements InvocationHandler {
......
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args)
throws Throwable {
......
//创建Hystrix命令
HystrixCommand
这个代理方法,我们一眼望穿秋水,原来是创建了HystrixCommand命令来进行目标方法的调用分发,这样就使得我们的整个方法调用具备了熔断能力,我们接着看看dispatch.get(method).invoke(args),这个分发器的定义是这样的Map
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 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负载均衡请求相关的调用过程
@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后续直接使用
@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负载均衡客户端的默认实现。
public class CachingSpringLoadBalancerFactory {
private volatile Map 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客户端配置
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原理则需要亲自分析相关的知识点,如果文章对你有所帮助,就点赞关注吧!
原文链接:https://www.jianshu.com/p/e3cf313b2238
相关资源:
GitOps 初探 - Dcsdn
行业分类-设备装置-一种用于数据库运维的移动平台及其使用方法 - Dcsdn
redis(哨兵模式配置) - Dcsdn
Redis数据的导出和导入 - Dcsdn