OpenFeign原理
Feign底层默认是JDK自带的HttpURLConnection,它是单线程发送HTTP请求的,不能配置线程池,我们使用
Ckhttp或者HttpClient来发送http请求,并且它们两个都支持线程池。
1、在 Spring 项目启动阶段,服务 A 的OpenFeign 框架会发起一个主动的扫包流程。
2、从指定的目录下扫描并加载所有被 @FeignClient 注解修饰的接口,然后将这些接口转换成 Bean,统一交给 Spring 来管理。
3、根据这些接口会经过 MVC Contract 协议解析,将方法上的注解都解析出来,放到 MethodMetadata 元数据中。
4、基于上面加载的每一个 FeignClient 接口,会生成一个动态代理对象,指向了一个包含对应方法的 MethodHandler 的 HashMap。MethodHandler 对元数据有引用关系。生成的动态代理对象会被添加到 Spring 容器中,并注入到对应的服务里。
5、服务 A 调用接口,准备发起远程调用。
6、从动态代理对象 Proxy 中找到一个 MethodHandler 实例,生成 Request,包含有服务的请求 URL(不包含服务的 IP)。
7、经过负载均衡算法找到一个服务的 IP 地址,拼接出请求的 URL
8、服务 B 处理服务 A 发起的远程调用请求,执行业务逻辑后,返回响应给服务 A。
(1)@EnableFeignClients 这个注解使用 Spring 框架的 Import 注解导入了 FeignClientsRegistrar 类,开始了 OpenFeign 组件的加载。PassJava 示例代码如下所示。
1 | // 启动类加上这个注解 |
(2)FeignClientsRegistrar 负责 Feign 接口的加载。
源码如下所示:
1 | @Override |
(3)registerFeignClients 会扫描指定包。
核心源码如下,调用 find 方法来查找指定路径 basePackage 的所有带有 @FeignClients 注解的带有 @FeignClient 注解的类、接口。
1 | Set<BeanDefinition> candidateComponents = scanner |
(4)只保留带有 @FeignClient 的接口。
1 | // 判断是否是带有注解的 Bean。 |
在创建 FeignClient Bean 的过程中就会去生成动态代理对象。调用接口时,其实就是调用动态代理对象的方法来发起请求的。
分析动态代理的入口方法为 getObject()。源码如下所示:
1 | Targeter targeter = get(context, Targeter.class); |
接着调用 target 方法这一块,里面的代码真的很多很细,我把核心的代码拿出来给大家讲下,这个 target 会有两种实现类:
DefaultTargeter 和 HystrixTargeter。而不论是哪种 target,都需要去调用 Feign.java 的 builder 方法去构造一个 feign client。
在构造的过程中,依赖 ReflectiveFeign 去构造。源码如下:
1 | // 省略部分代码 |
ReflectiveFeign 做的工作就是为带有 @FeignClient 注解的接口,创建出接口方法的动态代理对象。
比如示例代码中的接口 StudyTimeFeignService,会给这个接口中的方法 getMemberStudyTimeList 创建一个动态代理对象。
1 | @FeignClient("passjava-study") |
创建动态代理的原理图如下所示:
解析 FeignClient 接口上各个方法级别的注解,比如远程接口的 URL、接口类型(Get、Post 等)、各个请求参数等。这里用到了 MVC Contract 协议解析,后面会讲到。
- 然后将解析到的数据封装成元数据,并为每一个方法生成一个对应的 MethodHandler 类作为方法级别的代理。相当于把服务的请求地址、接口类型等都帮我们封装好了。这些 MethodHandler 方法会放到一个 HashMap 中。
- 然后会生成一个 InvocationHandler 用来管理这个 hashMap,其中 Dispatch 指向这个 HashMap。
- 然后使用 Java 的 JDK 原生的动态代理,实现了 FeignClient 接口的动态代理 Proxy 对象。这个 Proxy 会添加到 Spring 容器中。
- 当要调用接口方法时,其实会调用动态代理 Proxy 对象的 methodHandler 来发送请求。
这个动态代理对象的结构如下所示,它包含了所有接口方法的 MethodHandler。