Feign是一个伪客户端,即它不做任何的请求处理。Feign通过处理注解生成request,从而实现简化HTTP API开发的目的,即开发人员可以使用注解的方式定制request api模板,在发送http request请求之前,feign通过处理注解的方式替换掉request模板中的参数,这种实现方式显得更为直接、可理解。
现在让我们通过代码来一探Feign的究竟
FeignClientsRegistrar
首先看一下@EnableFeignClients
注解:
1 | (RetentionPolicy.RUNTIME) |
它导入了FeignClientsRegistrar
,这是一个用于注册beanDefinition的类,代码如下:
1 | public void registerBeanDefinitions(AnnotationMetadata metadata, |
registerDefaultConfiguration
方法注册FeignClientSpecification
的beanDefinition。
registerFeignClients
方法代码如下:
1 | public void registerFeignClients(AnnotationMetadata metadata, |
registerFeignClients
方法从basePackage中扫描被@FeignClient
注释的类:
- 调用
registerClientConfiguration
注册@FeignClient
注释中指定的configuration - 调用
registerFeignClient
注册该类的beanDefinition
registerFeignClient
方法的代码如下:
1 | private void registerFeignClient(BeanDefinitionRegistry registry, |
registerFeignClient
方法将注解的信息连同类名一起取出,赋给BeanDefinitionBuilder,然后根据BeanDefinitionBuilder得到beanDefinition,最后注册beanDefinition。
ReflectiveFeign
当使用Feign Client时,如上文的示例:
1 |
|
使用ReflectiveFeign
新建一个ComputeClient
的bean实例:
1 | public <T> T newInstance(Target<T> target) { |
在ReflectiveFeign
的newInstance
方法中,使用JDK代理创建一个代理类,其中的handler是ReflectiveFeign#FeignInvocationHandler
。
FeignInvocationHandler
前文我们看到,通过@Autowired
注入的ComputeClient
是一个代理类,它的handler是ReflectiveFeign#FeignInvocationHandler
,其中的拦截器方法如下:
1 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
实际上它将方法的运行委托给了dispatch中的MethodHandler
来执行,这里MethodHandler
的实现类是SynchronousMethodHandler
。
SynchronousMethodHandler
SynchronousMethodHandler
类是方法真正执行的类,其invoke
方法如下:
1 | public Object invoke(Object[] argv) throws Throwable { |
- 根据参数生成
RequestTemplate
对象,该对象就是http请求的模板 - 调用
executeAndDecode
方法发送请求并返回响应结果
executeAndDecode
executeAndDecode
方法通过RequestTemplate
生成Request
对象。
然后调用Client.execute
方法发送请求,接收Response
。
最后根据Feign方法的返回值来解码Response
,即将请求返回的响应转成需要的类型返回。
Client组件
Client组件是一个非常重要的组件,Feign最终发送request请求以及接收response响应,都是由Client组件完成的。Client的实现类为LoadBalancerFeignClient
:
1 |
|
默认情况下,注入Client.Default()
,它使用的网络请求框架为HttpURLConnection。
怎样使用其他的网络请求框架呢?
我们来看看HttpClientFeignLoadBalancedConfiguration
:
1 |
|
从代码的@ConditionalOnClass(ApacheHttpClient.class)
注释可以看出,只需要在pom文件中加上HttpClient的classpath就行了,另外需要在配置文件上加上feign.httpclient.enabled = true
,从@ConditionalOnProperty
注释可以看出,这个配置可以不写,在默认情况下就是true
。
因此,如果要使用HttpClient,在pom文件上加上:
1 | <dependency> |
同理,我们来看看OkHttpFeignLoadBalancedConfiguration
:
1 |
|
只需要在pom文件中加上OkHttp的classpath就行了,另外需要在配置文件上加上feign.okhttp.enabled = true
,这个配置不能省略,因为它默认不是true
。
因此,如果要使用Okhttp,在pom文件上加上:
1 | <dependency> |
LoadBalancerFeignClient
SynchronousMethodHandler
的executeAndDecode
方法中调用LoadBalancerFeignClient.execute
方法执行请求。
1 | public Response execute(Request request, Request.Options options) throws IOException { |
lbClient()
方法获取FeignLoadBalancer
实例,然后调用其中的executeWithLoadBalancer
方法:
1 | public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException { |
LoadBalancerCommand.submit
方法中以负载均衡的方式选择服务实例,其主要功能在selectServer()
方法中:
1 | private Observable<Server> selectServer() { |
服务实例的选择由LoadBalancerContext.getServerFromLoadBalancer
方法完成,其中的流程与Ribbon选择服务实例的步骤基本一致。
回到executeWithLoadBalancer
方法,服务实例确定之后,调用FeignLoadBalancer.execute
方法执行请求:
1 | public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride) |
首先调用request.client()
获取Client,前面说到这里是Client是Client.Default
。调用其中的execute
方法执行真正的请求并返回响应:
1 | public Response execute(Request request, Options options) throws IOException { |