在springcloud体系下服务之间的调用,目前比较常用的都是通过openfeign来进行调用,而openfeign是集成有负载均衡ribbon、熔断器hystrix的,那今天就和大家一起探究下 openFeign是如何与ribbon和hystrix一起协作来完成一起请求过程的。
在请求过程中接口需不需要熔断,发起请求的组件会有不同的配置,我们以启用hystrix以及okhttp的方式发起调用,如下配置
#熔断器启用
feign.hystrix.enabled=true
#启用okhttp
feign.okhttp.enabled=true
先看下上面两项配置干了些什么
首先来看feign.okhttp.enabled=true
定义了 LoadBalancerFeignClient 的Bean对象 ,LoadBalancerFeignClient 中的Client对象就只在这初始化的,源码中也就是OKHttpClient对象。
再看看feign.hystrix.enabled=true
接下来,我们在这个前提下捋一捋整个调用过程,openFeign从EnableFeignClients这个注解开始的
一、解析阶段
看代码import了一个类FeignClientsRegistrar,这个类实现了ImportBeanDefinitionRegistrar接口,那么就需要重写registerBeanDefinitions方法。如下:
这个方法做了2两件事情:
1、注册全局配置,如果EnableFeignClients注解上配置了defaultConfiguration属性
2、注册FeignClients
这个方法会扫描类路径上标记@FeignClient注解的接口,根据@EnableFeignClients注解上的配置,并循环注册FeignClient。
这个方法就是将FeignClient注解上的属性信息,封装到BeanDefinition中,并注册到Spring容器中。
二、运行阶段
在解析阶段将@FeignClient注解的类以FeignClientFactoryBean的形式往BeanFactory注入BeanDefinition。
1、获取在FeignAutoConfiguration中注入的FeignContext
2、调用feign方法,获取构建器
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;
}
根据FeignContext处理了我们自定义的@FeignClient以及全局@EnableFeignClients中配置的日志,加、解密器,验证器等,最后configureFeign如下,处理了链接超时、读取超时等配置项:
protected void configureFeign(FeignContext context, Feign.Builder builder) {
FeignClientProperties properties = this.applicationContext.getBean(FeignClientProperties.class);
if (properties != null) {
if (properties.isDefaultToProperties()) {
configureUsingConfiguration(context, builder);
configureUsingProperties(
properties.getConfig().get(properties.getDefaultConfig()),
builder);
configureUsingProperties(properties.getConfig().get(this.contextId),
builder);
} else {
configureUsingProperties(
properties.getConfig().get(properties.getDefaultConfig()),
builder);
configureUsingProperties(properties.getConfig().get(this.contextId),
builder);
configureUsingConfiguration(context, builder);
}
} else {
configureUsingConfiguration(context, builder);
}
}
3、调用loadBalance方法
4、根据Targeter(这里是HystrixTargeter),
调用的是feign的target方法,由于开启了hystrix所以实际上调用的是HystrixFeign的target方法
可以看到首先调用ParseHandlersByName的apply方法构建给每个方法都构建一个MethodHander对象,存储在Map中,key即为methodd对象,下面是其实现
这里的factory其实就是SynchronousMethodHandler的一个内部类
进入查看create方法的实现
可以看到实际上返回的是SynchronousMethodHandler对象,接下来就是
使用InvocationHandlerFactory工厂创建代理对象,返回InvocationHandler对象实际上是HystrixInvocationHandler,最后返回代理对象。
5、HystrixInvocationHandler的invoker方法
可见openFeign是通过HystrixInvocationHandler与Hystrix协作的,构建HystrixCommand对象,在run方法中执行核心逻辑,如果需要降级则执行getFallback方法。在run方法中根据method获取MethodHandler对象,这里是SynchronousMethodHandler对象,下面是invoke方法
这里调用的client的execute方法来处理请求,而client实际上是 LoadBalancerFeignClient,我们来看下其execute的实现,
再调用lbClient方法根据clientName获取到FeignLoadBalancer对象,进而调用其executeWithLoadBalancer方法
FeignLoadBalancer继承AbstractLoadBalancerAwareClient,executeWithLoadBalancer方法在AbstractLoadBalancerAwareClient中实现
方法中构建LoadBalancerCommand对象,并调用AbstractLoadBalancerAwareClient.this.execute方法,也就是OkHttpLoadBalancingClient的execute方法,可见openFeign是通过LoadBalancerFeignClient实现与Ribbon协作的
最终通过OkHttpClient发送请求,并返回结果封装到OkHttpRibbonResponse对象中,
总结,openFeign是通过HystrixInvocationHandler构建HystrixCommand完成与Hystrix的协作,通过LoadBalancerFeignClient实现与Ribbon协作的。