feign学习
前言
Feigin是一个声明式HTTP客户端,简化了远程调用,让编写HTTP客户端变得简单。
使用自定义接口,然后在上面添加注解,感知上就像调用一个接口一样。
Feign的简单实现
maven依赖
<!-- feign核心包 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>10.4.0</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jackson</artifactId>
<version>10.2.0</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>10.2.0</version>
</dependency>
请求接口
import com.feign.model.User;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
public interface IService {
@RequestLine("GET /user/info?id={id}")
@Headers("Content-Type: application/json")
User userInfo(@Param(value = "id") String id);
}
配置类
import feign.Feign;
import feign.Logger;
import feign.Request;
import feign.Retryer;
import feign.httpclient.ApacheHttpClient;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignService {
@Bean
public IService getFeignService() {
return Feign.builder()
.client(new ApacheHttpClient())
.encoder(new JacksonEncoder())
.decoder(new JacksonDecoder())
.logger(new Logger.NoOpLogger())
.logLevel(Logger.Level.FULL)
.retryer(Retryer.NEVER_RETRY)
.options(new Request.Options(1000, 3500))
.target(IService.class, "http://localhost:8080");
}
}
Controller
import com.feign.model.User;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/info")
@ResponseBody
public User info(@RequestParam("id") String id) {
return User.of().setId(id)
.setName("xixixi");
}
}
调用Test
import com.feign.model.User;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.feign.client.IService;
import org.springframework.test.context.junit4.SpringRunner;
@SpringBootTest
@RunWith(SpringRunner.class)
public class Test {
@Autowired
private IService iService;
@org.junit.Test
public void test() {
User s = iService.userInfo("001");
System.out.println(s);
}
}
从初始化构建实例和HTTP请求两个具体实现进行源码分析
构建实例–生成接口代理对象
在实现上,Feign是一个用于生成目标实例Feign#newInstance()的工厂,这个生成的实例便是接口的代理对象。
初始化构建实例
feign.Feign.Builder.java
/**
* 初始化构建实例,参数都可以自定义了builder的时候传进来
*/
public static class Builder {
// 请求模板的拦截器,默认为空
private final List<RequestInterceptor> requestInterceptors = new ArrayList<RequestInterceptor>();
// feign的日志等级,默认不打印日志
private feign.Logger.Level logLevel = feign.Logger.Level.NONE;
// 提取器.判断注解定义是否有效,并将有效信息组装成 List<MethodMetadata>
private Contract contract = new Contract.Default();
// http请求客户端.默认使用JDK的HttpURLConnection
private Client client = new Client.Default(null, null);
// 重试.默认开启,100ms重试一次,一共重试5次,最长持续1s
private Retryer retryer = new Retryer.Default();
// 日志记录器
private feign.Logger logger = new feign.Logger.NoOpLogger();
// 编码器.默认值支持String类型的编码.参数没有标注@Param注解时会交给编码器处理
private Encoder encoder = new Encoder.Default();
// 解码器.默认只能解码String和字节数组
private Decoder decoder = new Decoder.Default();
// 把@QueryMap标注的对象编码为Map查询参数明到值的映射
private QueryMapEncoder queryMapEncoder = new QueryMapEncoder.Default();
// 包装异常并往上抛
private ErrorDecoder errorDecoder = new ErrorDecoder.Default();
// 连接超时设置.默认10s链接超时,60s读取超时
private Request.Options options = new Request.Options();
// 控制反射方法调度,默认实现InvocationHandlerFactory
private InvocationHandlerFactory invocationHandlerFactory = new InvocationHandlerFactory.Default();
// 默认不会解码404
private boolean decode404;
private boolean closeAfterDecode = true;
// 异常传播策略,默认不包装不处理直接抛出
private ExceptionPropagationPolicy propagationPolicy = NONE;
...
/**
* 获得所需要代理接口的代理类
* @param apiType
* @param url
* @param <T>
* @return
*/
public <T> T target(Class<T> apiType, String url) {
return target(new Target.HardCodedTarget<T>(apiType, url));
}
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
/**
* 获得ReflectiveFeign的实例
* @return
*/
public Feign build() {
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404, closeAfterDecode, propagationPolicy);
ReflectiveFeign.ParseHandlersByName handlersByName =
new ReflectiveFeign.ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
}
通过动态代理反射创建接口类实例
feign.ReflectiveFeign
ReflectiveFeign是Feign的唯一实现,实现了Feign#newInstance,创建一个target接口的代理对象。
public class ReflectiveFeign extends Feign {
// 提供方法,给接口的每个方法生成一个处理器
private final ParseHandlersByName targetToHandlersByName;
// 调度中心
private final InvocationHandlerFactory factory;
private final QueryMapEncoder queryMapEncoder;
/**
* creates an api binding to the {@code target}. As this invokes reflection, care should be taken
* to cache the result.
* 创建到目标的API绑定。由于这将调用反射,因此应该小心缓存结果。
*/
@Override
public <T> T newInstance(Target<T> target) {
// 拿到该接口的所有方法的处理器。解析target的各种注解、方法,放到MethodMetadata里面
// 这个里面的MethodHandler的实现为SynchronousMethodHandler
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
// 拿到需要处理调用的方法处理器
// 这个里面的MethodHandler的实现为DefaultMethodHandler
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
// 默认方法处理器
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
// 遍历处理方法
for (Method method : target.type().getMethods()) {
if (method.getDeclaringClass() == Object.class) {
continue;
} else if (Util.isDefault(method)) {
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
// 为该目标接口类型创建一个InvocationHandler
InvocationHandler handler = factory.create(target, methodToHandler);
// 为接口创建一个代理对象.target.type()就是请求接口类
// newProxyInstance的三个参数:
// loader: 用哪个类加载器去加载代理对象
// interfaces:动态代理类需要实现的接口
// h:动态代理方法在执行时,会调用h里面的invoke方法去执行
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
// DefaultMethodHandler通过调用接口默认方法代码处理方法
// 把目标对象绑定到方法句柄(MethodHandle)上。必须为DefaultMethodHandler的给定实例调用一次且仅调用一次
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
}
factory.create()创建方法处理
SynchronousMethodHandler.class
final class SynchronousMethodHandler implements MethodHandler {
private static final long MAX_RESPONSE_BUFFER_SIZE = 8192L;
// 方法元信息
private final MethodMetadata metadata;
...
// 请求拦截器,在target.apply(template),模板->请求的转换前完成拦截,而不是在发送请求前一刻
// 内置仅有一个实现:BasicAuthRequestInterceptor用于鉴权
private final feign.RequestTemplate.Factory buildTemplateFromArgs;
private SynchronousMethodHandler(Target<?> target, Client client, Retryer retryer,
List<RequestInterceptor> requestInterceptors, Logger logger,
Logger.Level logLevel, MethodMetadata metadata,
RequestTemplate.Factory buildTemplateFromArgs, Options options,
Decoder decoder, ErrorDecoder errorDecoder, boolean decode404,
boolean closeAfterDecode, ExceptionPropagationPolicy propagationPolicy) {
this.target = checkNotNull(target, "target");
this.client = checkNotNull(client, "client for %s", target);
this.retryer = checkNotNull(retryer, "retryer for %s", target);
this.requestInterceptors =
checkNotNull(requestInterceptors, "requestInterceptors for %s", target);
this.logger = checkNotNull(logger, "logger for %s", target);
this.logLevel = checkNotNull(logLevel, "logLevel for %s", target);
this.metadata = checkNotNull(metadata, "metadata for %s", target);
this.buildTemplateFromArgs = checkNotNull(buildTemplateFromArgs, "metadata for %s", target);
this.options = checkNotNull(options, "options for %s", target);
this.errorDecoder = checkNotNull(errorDecoder, "errorDecoder for %s", target);
this.decoder = checkNotNull(decoder, "decoder for %s", target);
this.decode404 = decode404;
this.closeAfterDecode = closeAfterDecode;
this.propagationPolicy = propagationPolicy;
}
static class Factory {
...
public MethodHandler create(Target<?> target,
MethodMetadata md,
RequestTemplate.Factory buildTemplateFromArgs,
Options options,
Decoder decoder,
ErrorDecoder errorDecoder) {
return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger,
logLevel, md, buildTemplateFromArgs, options, decoder,
errorDecoder, decode404, closeAfterDecode, propagationPolicy);
}
}
}
方法请求处理
invoke执行
invoke执行时利用ReflectiveFeign反射类实现InvocationHandler的invoke方法。是最外层的请求处理,会拿到dispatch中对一个对应的method执行。
static class FeignInvocationHandler implements InvocationHandler {
private final Target target;
private final Map<Method, MethodHandler> dispatch;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
...
// 通过放射拿到dispatch的map里面method对应的MethodHandler,执行
return dispatch.get(method).invoke(args);
}
}
dispatch.get(method)对应的MethodHandler实现类为SynchronousMethodHandler(同步方法调用处理器)。会构建方法请求模板,执行http请求,并完成解码,如果请求失败,会进行重试处理。
final class SynchronousMethodHandler implements MethodHandler {
public Object invoke(Object[] argv) throws Throwable {
// 根据方法入参,构建请求模板
RequestTemplate template = this.buildTemplateFromArgs.create(argv);
// 获取参数里面第一个Options类型的参数
Options options = this.findOptions(argv);
// 克隆一个重试
Retryer retryer = this.retryer.clone();
while(true) {
try {
// 执行发送HTTP请求,并且完成解码
return this.executeAndDecode(template, options);
} catch (RetryableException var9) {
// 重试相关处理
...
}
}
}
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
// 构建方法模板,解析注解,拿到httpMethod、url、headers、body
// 里面会执行所有拦截器,完成模板定制
Request request = this.targetRequest(template);
...
Response response;
try {
// 发送HTTP请求.Feign自己的client是ApacheHttpClient,也可以自己构造
response = this.client.execute(request, options);
} catch (IOException var16) {
...
可以做一些错误处理
}
// 是否需要关闭流
boolean shouldClose = true;
try {
// 对response进行解析
} finally {
// 关闭流并且释放与其相关的任何方法
if (shouldClose) {
Util.ensureClosed(response.body());
}
}
}
// 构建模板
Request targetRequest(RequestTemplate template) {
// 执行请求拦截器,对模板做一些加工处理
Iterator var2 = this.requestInterceptors.iterator();
while(var2.hasNext()) {
RequestInterceptor interceptor = (RequestInterceptor)var2.next();
interceptor.apply(template);
}
// 执行实现类是HardCodedTarget硬编码目标类
// 如果请求模版的URL不含有http,也就是说是个相对路径,这里就把Base Url加进去
// 如果请求模版已经是绝对路径了,那就不管啦
return this.target.apply(template);
}
Object decode(Response response) throws Throwable {
try {
return this.decoder.decode(response, this.metadata.returnType());
} catch (FeignException var3) {
throw var3;
} catch (RuntimeException var4) {
throw new DecodeException(response.status(), var4.getMessage(), response.request(), var4);
}
}
Options findOptions(Object[] argv) {
return argv != null && argv.length != 0 ? (Options)Stream.of(argv).filter((o) -> {
return o instanceof Options;
}).findFirst().orElse(this.options) : this.options;
}
}
}
发送http请求
ApacheHttpClient是Feign实现的Client类,会发送http请求并处理返回Response。
public final class ApacheHttpClient implements Client {
public Response execute(Request request, Options options) throws IOException {
HttpUriRequest httpUriRequest;
try {
// 生成HttpUriRequest
//toHttpUriRequest每次都会通过RequestBuilder创建一个新实例,是线程安全的
httpUriRequest = this.toHttpUriRequest(request, options);
} catch (URISyntaxException var5) {
throw new IOException("URL '" + request.url() + "' couldn't be parsed into a URI", var5);
}
// 发送http请求
HttpResponse httpResponse = this.client.execute(httpUriRequest);
// 返回Response
return this.toFeignResponse(httpResponse, request);
}
HttpClient
ApacheHttpClient的默认client是apache的HttpClient的CloseableHttpClient。之后对http的请求也是执行的apache.HttpClient包的实现了。
这就是Feign整个的执行逻辑,处理Feign本身的这些,还用到了代理Proxy还有apache的HttpClient,这两个点也很值得深入研究一下。
参考
享学Feign