-
Feign远程调用,核心就是通过一系列的封装和处理,将以JAVA注解的方式定义的远程调用API接口,最终转换成为HTTP的请求形式,然后将HTTP的请求的响应结果,解码成JAVA Bean,放回给调用者。
-
流程
- @FeignClient远程调用接口
- JDK Proxy 动态代理实例
- 实现了远程调用接口,通过InvokeHandler调用处理器分发远程调用
- InvokeHandler
- 根据远程API的Method方法实例,进行MethodHandler方法处理器的分发
- MethodHandler
- ReuqestTemplate
- 根据实参,构造Request实例
- Encoder
- Reuqest请求的编码
- Interceptors,Logger
- 负责对请求和返回进行拦截处理,日志记录
- feign.Client
- 基于负载均衡,重试器,以及不同的HTTP请求框架,发送HTTP请求
- http request
- 远程server
- http response
- feign.Client
- Decoder
- response的解码
- Interceptors,Logger
- MethodHandler
- InvokeHandler
- JDK Proxy动态代理实例
- Resonse Bean
-
即Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数在应用到请求上,进而转化成真正的Request请求,通过Feign以及JAVA的动态代理机制,使得Java开发人员,可以不通过HTTP框架去封装HTTP请求报文的方式,完成远程服务的HTTP调用
-
在微服务启动时,Feign会进行包扫描,对加@FeignClient注解的接口,按照注解的规则,创建远程接口的本地JDK Proxy代理实例,然后将这些本地Proxy代理实例,注入到Spring IOC容器中,当远程接口的方法调用,由Proxy代理实例去完成真正的远程访问,并且返回结果
-
远程接口的本地JDK Proxy代理实例,有以下特点:
- Proxy代理实例,实现了一个加@FeignClient注解的远程调用接口
- Proxy代理实例,能在内部进行HTTP请求的封装,以及发送HTTP请求
- Proxy代理实例,能处理远程HTTP请求的响应,并且完成结果的解码,然后返回给调用者
-
使用:
- 加上@FeignClient注解,也就是说,Feign在启动时,会为其创建一个本地JDK Proxy代理实例,并注册到Spring IOC容器
- 通过@Resource注解,按照类型匹配,从Spring IOC容器找到这个代理实例,并且装配给需要的成员变量
- 为了创建Feign的远程接口的代理实现类,Feign提供了自己的一个默认的调用处理器,叫做FeignInvocationHandler类,该类处于feign-core核心包中,当然,调用处理器可以进行替换,如果Feign与Hystrix结合使用,则会替换成HystrixInvocationHandler调用处理器类,类处于feign-hystrix的jar包中。
-
FeignInvocationHandler是一个相对简单的类,有一个非常重要的Map类型成员dispatch映射(private final Map<Method, MethodHandler> dispatch;),保存着远程接口方法到MethodHandler方法处理器的映射
-
默认的FeignInvocationHandler,在处理远程方法调用的时候,会根据Java反射的方法实例,在dispatch映射对象中,找到对应的MethodHandler方法处理器,然后交给MethodHandler,完成实际的HTTP请求和结果的处理,远程调用接口中有几个方法,其代理实现类的调用处理器FeignInvocationHandler的dispatch成员,就有几个key-value键值对
-
invoke方法
- 首先,根据方法实例,从方法实例对象和方法处理器的映射中,取得方法处理器,然后调用方法处理器的invoke方法
- dispatch.get(method).invoke(args)
- 根据java反射的方法实例,在dispatch映射对象中,找到对应的MethodHandler方法处理器
- 调用MethodHandler方法处理器的invoke()方法,完成实际的Http请求和结果的处理
- MethodHandler方法处理器,和JDK动态代理机制中位于java.lang.reflect包的InvocationHandler调用处理器接口,没有任何继承和实现关系,MethodHandler仅仅是Feign自定义的,一个非常简单的接口
-
方法处理器MethodHandler
-
Feign的方法处理器MethodHandler是一个独立的接口,定义在InvocationHandlerFactory接口中,仅仅拥有一个invoke()方法
-
MethodHandler的invoke()方法,主要职责是完成实际远程URL请求,然后返回解码后的远程URL的响应结果,Feign提供了默认的SynchronousMethodHandler实现类,提供了基本的远程URL的同步请求处理,
-
final class SynchronousMethodHandler implements MethodHandler {//…// 执行Handler 的处理 public Object invoke(Object[] argv) throws Throwable {RequestTemplate requestTemplate = this.buildTemplateFromArgs.create(argv);Retryer retryer = this.retryer.clone();while(true) {try {return this.executeAndDecode(requestTemplate);} catch (RetryableException var5) {//…省略不相干代码}} }//执行请求,然后解码结果 Object executeAndDecode(RequestTemplate template) throws Throwable {Request request = this.targetRequest(template);long start = System.nanoTime();Response response;try {response = this.client.execute(request, this.options);response.toBuilder().request(request).build();} } }
-
SynchronousMethodHandler的invoke方法,调用了自己的executeAndDecode()请求执行结果解码方法
- 首先通过RequestTemplate请求模板实例,生成远程URL请求实例request
- 然后用自己的feign客户端client成员,execute()执行请求,并且获取response响应
- 对response响应进行结果解码
-
-
Feign客户端组件feign.Client
-
客户端组件是Feign中一个非常重要的组件,负责端到端的执行URL请求,其核心逻辑:发送request请求到服务器,并接收response响应后进行解码
-
feign.Client类,是代表客户端的顶层接口,只有一个抽象方法
-
public interface Client {//提交HTTP请求,并且接收response响应后进行解码Response execute(Request request, Options options) throws IOException;}
-
由于不同的feign.Client实现类,内部完成HTTP请求的组件和技术不同,因此,feign.Client有多个不同的实现,
- Client.Default类:默认的feign.Client客户端实现类,内部使用HttpURLConnection完成URL请求处理
- ApacheHttpClient类:内部使用Apache httpclient开源组件完成URL请求处理的feign.Client客户端实现类
- OkHttpClient类:内部使用OkHttp3开源组件完成URL请求处理的feign.Client客户端实现类
- LoadBalancerFeignClient类:内部使用Ribbon负载均衡技术完成URL请求处理的feign.Client客户端实现类
-
-
Client.Default类:
- 作为默认的Client接口实现类,在Client.Default内部使用JDK自带的HttpURLConnection类实现URL网络请求
- 在JDK1.8中,虽然在HttpURLConnection底层,使用了非常简单的HTTP连接池技术,但是其HTTP连接的复用能力,实际是非常弱的,性能当然也很低
-
ApacheHttpClient类
- ApacheHttpClient客户端类的内部,使用Apache HttpClient开源组件完成URL请求的处理
- 从代码开发的角度而言,Apache HttpClient相比较传统JDK自带的URLConnection,增加了易用性和灵活性,他不仅使客户端发送Http请求变得容易,而且也方便开发人员测试接口,即提高了开发效率,也方便提高代码的健壮性
- 从性能角度而言,ApacheHttpClient带有连接池的功能,具备优秀的HTTP连接的复用能力
- ApacheHttpClient类处于feign-httpclient的专门的jar包中,如果使用,还需要通过Maven依赖或者其他的方式,导入配套版本的专门的jar包
-
OkHttpClient类
- OkHttpClient客户端类的内部,使用OkHttp3开源组件完成URL请求处理,OkHttp3开源组件由Square公司开发,用于替代HttpUrlConnection和ApacheHttpClient,由于OkHttp3较好的支持SPDY协议(SPDY是Google开发的基于TCP的传输层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验),从Android4.4开始,google已经开始将Android源码中的HttpURLConnection请求类使用OkHttp进行了替换,也就是说,对于Android移动端APP开发来说,OkHttp3组件,世纪初的开发组件之一
-
LoadBalancerFeignClient
- LoadBalancerFeignClient内部使用了Ribbon客户端负载均衡技术完成URL请求处理,在原理上,简单的使用了delegate包装代理模式,Ribbom负载均衡组件计算出合适的服务端server后,由内部包装类delegate代理客户端完成到服务端server的HTTP请求,所封装的delegate客户端代理实例的类型,可以是Client.Default默认客户端,也可以是ApacheHttpClient客户端类或OkHttpClient高性能客户端类,还可以其他的定制的feign.Client客户端实现类型
-
Feign远程调用的执行流程
- 由于Feign远程调用接口的JDK Proxy实例的InvokeHandler调用处理器有很多种,导致Feign远程调用的执行流程,也稍微有所区别,但是远程调用执行流程的主要步骤,是一致的
- 与FeignInvocationHandler相关的远程调用执行流程
- FeignInvocationHandler是默认的调用处理器,如果不对Feign做特殊的配置,则Feign将使用此调用处理器,
- 第一步:通过Spring IOC容器实例,装配代理实例,然后进行远程调用
- Feign在启动时,会为加上了@FeignClient注解的所有远程接口,创建一个本地JDK Proxy代理实例,并注册到Spring IOC容器
- 然后本实例的controller代用代码中,通过@Resouce注解,按照类型或者名称进行匹配,从Spring IOC容器找到这个代理实例,并且装配给@Resource注解所在的成员变量,调用JDK Proxy动态代理实例的比如hello()方法
- 第二步:执行InvokeHandler调用处理器的invoke方法
- JDK Proxy动态代理实例的真正的方法调用过程,具体是通过InvkeHandler调用处理器完成的,所以这里的clientProxy代理实例,会调用到默认的FeignInvocationHandler调用处理器实例的invoke方法
- 默认的调用处理器FeignInvocationHandler,内部保持了一个远程调用方法实例和方法处理器的一个key-value键值对的Map映射,FeignInvocationHandler在其invoke方法中,会根据java反射的方法实例,在dispatch映射对象中,找到对应的MethodHandler方法处理器,然后由后者完成实际的HTTP请求和结果的处理
- 所以,FeignInvocationHandler会从自己的dispatch映射中,找到hello()方法所对应的MethodHandler方法处理器,然后调用invoke方法
- 第三步:执行MethodHandler方法处理器的invoke方法
- feign默认的方法处理器为SynchronousMethodHandler,其invoke方法主要是通过内部成员feign客户端成员client,完成远程URL请求执行和获取远程结果
- feign.Client客户端有多种类型,不同的类型,完成URL请求处理的具体方式不同
- 第四步:通过feign.Client客户端成员,完成远程URL请求执行和获取远程结果
- 如果MethodHandler·方法处理器实例中declient客户端,是默认的feign.Client.Default实现类,则使用JDK自带的HttpURLConnection类,完成远程URL请求执行和获取远程结果
- 如果MethodHandler方法处理器实例中的client客户端,是ApacheHttpClient客户端实现类,则使用ApacheHttpClient开源组件,完成远程URL请求执行和获取远程结果
- SynchronousMethodHandler并不是直接完成远程URL的请求,而是通过负载均衡机制,定位到合适的远程server服务器,然后再完成真正的远程URL请求,换句话说,SynchronousMethodHandler实例的client成员,其实例不是feign.Client.Default类型,而是LoadBalancerFeignClient客户端负载均衡类型,因此,上面的第三步:如果进一步细分的话,大致如下:
- 首先通过SynchronousMethodHandler内部的client实例,实质为负责客户端负载均衡LoadBalancerFeignClient实例,首先查找到远程的server服务端,
- 然后再由LoadBalancerFeignClient实例内部包装的feign.Client.Default内部类实例,去请求server端服务器,完成URL请求处理
- 默认的FeignInvocationHandler相关的远程调用执行流程,在运行机制以及调用性能上,满足不了生产环境的要求
- 没有远程调用过程中的熔断检测和恢复机制
- 也没有用到高性能的HTTP连接池技术