1. OpenFeign的源码解析
关于OpenFeign的源码解析这位博主写的非常详细,可先阅读该博客【OpenFeign调用服务的核心原理解析】,本文对其内容做了概括整理,较于源码解析 通俗易懂。
2. Feign远程调用的基本流程图解
Feign远程调用,核心就是通过一系列的封装和处理,将以Java注解方式定义的远程调用API接口,最终转换成HTTP的请求形式,然后将HTTP请求的响应结果,解码成Java Bean,返回给调用者。
Feign远程调用的基本流程,简单可以分成两步:一是生成Feign的动态代理;二是Feign执行。如下图所示:
从上图可以看到,Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的Request请求。通过Feign以及Java的动态代理机制,可以不用通过HTTP框架去封装HTTP请求报文的方式,完成远程服务的HTTP调用。
3. 与FeignInvocationHandler相关的远程调用执行流程
Feign远程调用接口的JDK Proxy实例的InvokeHandler调用处理器有多种,导致Feign远程调用的执行流程稍微有点区别,但是远程调用执行流程的主要步骤基本是一致的。这里主要介绍与默认的调用处理器FeignInvocationHandler相关的远程调用执行流程。
结合下面ICardFeignClient的JDK Proxy远程动态代理实例的getCardInfoByUserId(@Param("id") String id)
方法远程调用,介绍一下调用执行过程:
@RestController
@RequestMapping("/test")
public class TestController {
@Autowired
private ICardFeignClient cardFeignClient;
/***
* @description: feign过程分析实例
* @param:
* @return:
* @author name silk
* @date: 2024/3/29 9:10
*/
@GetMapping("/feign")
public String testFeign(String userId) {
String cardInfo = cardFeignClient.getCardInfoByUserId(userId);
return cardInfo;
}
}
@FeignClient(value = "card-service", path = "/card")
public interface ICardFeignClient {
@PostMapping("/getCard")
String getCardInfoByUserId(@Param("id") String id);
}
第1步:通过Spring IOC 容器实例,装配代理实例,然后进行远程调用。
-
Feign在启动时,会为加上了@FeignClient注解的所有远程接口(如本例的ICardFeignClient接口),创建一个本地JDK Proxy代理实例,并注册到Spring IOC容器。在这里,暂且将这个Proxy代理实例,叫做CardProxy。这个代理的创建过程可参考推荐博客【OpenFeign调用服务的核心原理解析】。
-
然后,在本实例的TestController调用代码中,通过@Autowired注解,按照类型或者名称进行匹配,从Spring IOC容器找到CardProxy这个代理实例,并且装配给@Autowired注解所在的成员变量,本实例的成员变量的名称为cardFeignClient。
-
TestController在进行getCardInfoByUserId()远程调用时,直接通过cardFeignClient成员变量,调用JDK Proxy动态代理实例的getCardInfoByUserId()方法。
第2步:执行InvokeHandler调用处理器的invoke()方法
-
JDK Proxy动态代理实例的真正的方法调用过程,具体是通过InvokeHandler调用处理器完成的。所以这里的CardProxy代理实例,会调用到默认的FeignInvocationHandler调用处理器实例的invoke()方法。
-
通过前面推荐博客中FeignInvocationHandler调用处理器的详细介绍,已经知道默认的调用处理器FeignInvocationHandler,内部保持了一个远程调用方法实例和方法处理器的一个Key-Value键值对Map映射。FeignInvocationHandler在其invoke()方法中,会根据Java反射的方法实例,在dispatch映射对象中,找到对应的MethodHandler方法处理器,然后由后者完成实际的HTTP请求和结果的处理。
-
所以在第2步中,FeignInvocationHandler会从自己的dispatch映射中,找到getCardInfoByUserId()方法所对应的MethodHandler方法处理器,然后调用其 invoke()方法。
第3步:执行MethodHandler方法处理器的invoke()方法
-
通过前面推荐博客关于MethodHandler方法处理器的非常详细的介绍,已经知道,feign默认的方法处理器为SynchronousMethodHandler,其invoke()方法主要是通过内部成员feign客户端成员client,完成远程URL请求执行和获取远程结果。(首先通过SynchronousMethodHandler内部的client实例,实质为负责客户端负载均衡LoadBalancerFeignClient实例,首先查找到远程的server服务端;然后再由LoadBalancerFeignClient实例内部包装的feign.Client.Default内部类实例,去请求server端服务器,完成URL请求处理。)
-
#targetRequest()方法在创建Request对象前,先执行Feign的拦截器逻辑
-
feign.Client客户端有多种类型,不同的类型完成URL请求处理的具体方式不同。
第4步:通过feign.Client客户端成员,完成远程URL请求执行和获取远程结果
- 如果MethodHandler方法处理器实例中的client客户端,是默认的feign.Client.Default实现类性,则使用JDK自带的HttpURLConnnection类,完成远程 URL 请求执行和获取远程结果;如果MethodHandler方法处理器实例中的client客户端,是ApacheHttpClient客户端实现类性,则使用Apache httpclient开源组件,完成远程URL请求执行和获取远程结果。其他的还有如,OkHttpClient类:内部使用OkHttp3开源组件完成URL请求处理的feign.Client客户端实现类。LoadBalancerFeignClient类:内部使用Ribbon负载均衡技术完成URL请求处理的feign.Client 客户端实现类。
- 还有一些特殊场景使用的feign.Client客户端实现类,也可以定制自己的feign.Client实现类。