OpenFeign使用及原理分析
概览
Feign是在RestTemplate基础上封装的,使用注解的方式来声明一组与服务提供者Rest接口所对应的本地Java API接口方法。Feign将远程Rest接口抽象成一个声明式的FeignClient(Java API)客户端,并且负责完成FeignClient客户端和服务提供方的Rest接口绑定。
使用
添加依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> <version>2.2.10.RELEASE</version> </dependency>
在启动类添加注解,指定feign basePackages路径
@EnableFeignClients(basePackages = "com.xw.order.feign")
JDK动态代理机制
动态代理实质是通过java.lang.reflect.Proxy#newProxyInstance
方法生成已给动态代理实例,相关参数如下:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
参数说明:
- 第一个参数为ClassLoader类加载器类型,此处的类加载器和被委托类的类加载器相同即可
- 第二个参数为Class[]类型,代表动态代理类将会实现的抽象接口,此接口是被委托类所实现的接口。
- 第三个参数为InvocationHandler类型,它的调用处理器实例将作为JDK生成的动态代理对象的内部成员,在对动态代理对象进行方法调用时,该处理器的invoke(...)方法会被执行。
示例说明:
声明EchoService及其实现类:
public interface EchoService {
void echo();
}
public class EchoServiceImpl implements EchoService {
@Override
public void echo() {
System.out.println("echo method");
}
}
编写调用处理器:
public class EchoServiceInvocationHandler implements InvocationHandler {
private EchoService echoService;
public EchoServiceInvocationHandler(EchoService echoService) {
this.echoService = echoService;
}
public EchoServiceInvocationHandler() {
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始代理调用");
echoService.echo();
return null;
}
}
编写测试类:
@Test
void testJdkProxy() {
EchoServiceImpl echoServiceImpl = new EchoServiceImpl();
EchoServiceInvocationHandler echoServiceInvocationHandler = new EchoServiceInvocationHandler(echoServiceImpl);
EchoService echoService =
(EchoService) Proxy.newProxyInstance(this.getClass().getClassLoader(), EchoServiceImpl.class.getInterfaces(), echoServiceInvocationHandler);
echoService.echo();
}
结果如下:
Feign重要组件
在分析Feign的执行流程之前,我们先对Feign的一些重要的组件进行了解。
Feign的调用处理器InvocationHandler
JDK Proxy生成动态代理核心是 定义一个调用处理器。Fegin提供了一个默认的调用处理器FeignInvocationHandler
,当Feign与Hystix组合使用时,将会使用HystrixInvocationHandler
,默认的调用处理器FeignInvocationHandler是一个相对简单的类,有一个非常重要的Map类型成员dispatch映射,保存着RPC方法反射实例到Feign的方法处理器MethodHandler实例的映射。key为client的method对象,value为该方法对应的方法处理器实例。
Feign的方法处理器MethodHandler
Feign的MethodHandler接口是Feign自定义接口,是一个非常简单的接口,只有一个invoke方法。内置的SynchronousMethodHandler
同步方法处理实现类是Feign的一个重要类,提供了基本的远程URL的同步请求响应处理。调用的核心代码如下:
@Override
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Options options = findOptions(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
return executeAndDecode(template, options);
} catch (RetryableException e) {
try {
retryer.continueOrPropagate(e);
} catch (RetryableException th) {
Throwable cause = th.getCause();
if (propagationPolicy == UNWRAP && cause != null) {
throw cause;
} else {
throw th;
}
}
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
客户端Client
客户端组件是Feign中一个非常重要的组件,负责最终的HTTP(包括REST)请求的执行。它的核心逻辑:发送Request请求到服务器,在接收到Response响应后进行解码,并返回结果。
常用的Feign客户端实现类如下:
- Client.Default类:默认的实现类,使用JDK的HttpURLConnnection类提交HTTP请求。
- ApacheHttpClient类:该客户端类在内部使用Apache HttpClient开源组件提交HTTP请求。
- OkHttpClient类:该客户端类在内部使用OkHttp3开源组件提交HTTP请求。
- LoadBalancerFeignClient类:内部使用Ribbon负载均衡技术完成HTTP请求处理。
建议
默认使用的Client.Default无法做到连接复用,请勿在生产环境使用。
Feign流程分析
通过应用启动类上的@EnableFeignClients注解开启Feign的装配和远程代理实例创建。在@EnableFeignClients注解源码中可以看到导入了FeignClientsRegistrar类,该类用于扫描@FeignClient注解过的RPC接口。
通过对@FeignClient注解RPC接口扫描创建远程调用的动态代理实例。FeignClientsRegistrar类会进行包扫描,扫描所有包下@FeignClient注解过的接口,创建RPC接口的FactoryBean工厂类实例,并将这些FactoryBean注入Spring IOC容器中。如果应用某些地方需要注入RPC接口的实例(比如被@Resource引用),Spring就会通过注册的FactoryBean工厂类实例的getObject()方法获取RPC接口的动态代理实例。在创建RPC接口的动态代理实例时,Feign会为每一个RPC接口创建一个调用处理器,也会为接口的每一个RPC方法创建一个方法处理器,并且将方法处理器缓存在调用处理器的dispatch映射成员中
发生RPC调用时,eign会根据RPC方法的反射实例从调用处理器的dispatch成员中取得方法处理器,然后由MethodHandler方法处理器开始HTTP请求处理。MethodHandler会结合实际的调用参数,通过RequesTemplate模板实例生成Request请求实例。最后,将Request请求实例交给feign.Client客户端实例进一步完成HTTP请求处理。client有feign.client.Default、ApacheHttpClient、OkHttpClient、LoadBalancerFeignClient等。
参考
- SpringCloud Feign官方文档
- Spring Cloud、Nginx核心编程