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 { |