Feign是向远程服务器发送请求的客户端,类似于RestTemplate,但是使用声明式的方式,实际使用的时候请求会根据声明的方法签名,注解这些动态构造请求。下面主要是分析Spring Cloud集成的Openfeign的一系列执行流程。
一个最基本的使用情况如下:
首先在配置类上启用Feign:
@EnableFeignClients
public class SpringCloudFeignDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudFeignDemoApplication.class, args);
}
}
然后创建一个接口,声明为FegnClient,这样Spring启动的时候会为它注册一个bean。
@FeignClient(name = "client", url = "http://localhost:8080")
@Component // There shouldn't be this annotation here, but this need to be here because of IDEA bug
public interface FeignClientX {
@GetMapping("print")
public void print(@SpringQueryMap Param param);
}
对于每一个FeignClient实例, Spring都会创建一个代理类ReflectiveFeign.FeignInvocationHandler代理对象,调用FeignClient方法时会被拦截处理,处理代码如下:
Class: ReflectiveFeign.FeignInvocationHandler
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("equals".equals(method.getName())) {
try {
Object otherHandler =
args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
} else if ("hashCode".equals(method.getName())) {
return hashCode();
} else if ("toString".equals(method.getName())) {
return toString();
}
return dispatch.get(method).invoke(args);
}
如果是equals()方法,则判断代理对象是否是同一个,如果是hashCode()和toString()方法,则调用代理对象的hashCode()和toString()方法,否则就查找对应方法并调用。
每个FeignInvocationHandler代理对象都有一个dispatch属性,其原型为:
private final Map<Method, MethodHandler> dispatch;
是在生成FeignClient代理对象的时候会处理每个方法上的注解,生成一个处理对象SynchronousMethodHandler放到此Map里,调用时根据方法签名对应的key来获取到具体的SynchronousMethodHandler对象。
而每个SynchronousMethodHandler又封装了发起请求的Client对象,重试处理器等:
private final MethodMetadata metadata;
private final Target<?> target;
private final Client client;
private final Retryer retryer;
private final List<RequestInterceptor> requestInterceptors;
private final Logger logger;
private final Logger.Level logLevel;
private final RequestTemplate.Factory buildTemplateFromArgs;
private final Options options;
private final Decoder decoder;
private final ErrorDecoder errorDecoder;
private final boolean decode404;
private final boolean closeAfterDecode;
private final ExceptionPropagationPolicy propagationPolicy;
当Feign代理对象通过dispatch属性找到对应的SynchronousMethodHandler对象的后,就会调用它的invoke方法,如果出错,并且配置了Retryer则还会重试,实现如下:
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
// 如果方法参数中有Options对象,则使用参数中的Options,否则使用默认的
Options options = findOptions(argv);
// 复制一个Retryer
Retryer retryer = this.retryer.clone();
while (true) {
try {
return executeAndDecode(template, options);
} catch (RetryableException e) {
try {
// 检查是否到达最大重试次数,如果超过则throw异常跳出循环,否则继续
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;
}
}
}
第一步根据MethodMetadata里保存的RequestTemplate以及传进来的参数argv构建RequestTemplate:
public RequestTemplate create(Object[] argv) {
// 依据MethodMetadata里面保存的RequestTemplate生成一个新的RequestTemplate。
RequestTemplate mutable = RequestTemplate.from(metadata.template());
// metadata.urlIndex() != null判断方法参数里是否有URI类型参数, 如果有则更新Url为URI的地址
if (metadata.urlIndex() != null) {
int urlIndex = metadata.urlIndex();
checkArgument(argv[urlIndex] != null, "URI parameter %s was null", urlIndex);
mutable.target(String.valueOf(argv[urlIndex]));
}
// 根据参数索引找到参数名称,然后从argv数组中取值,最后把名和值对应放入varBuilder中
Map<String, Object> varBuilder = new LinkedHashMap<String, Object>();
for (Entry<Integer, Collection<String>> entry : metadata.indexToName().entrySet()) {
int i = entry.getKey();
Object value = argv[entry.getKey()];
if (value != null) { // Null values are skipped.
if (indexToExpander.containsKey(i)) {
value = expandElements(indexToExpander.get(i), value);
}
for (String name : entry.getValue()) {
varBuilder.put(name, value);
}
}
}
// 解析URL, header, body等中的占位符
RequestTemplate template = resolve(argv, mutable, varBuilder);
if (metadata.queryMapIndex() != null) {
// add query map parameters after initial resolve so that they take
// precedence over any predefined values
Object value = argv[metadata.queryMapIndex()];
Map<String, Object> queryMap = toQueryMap(value);
template = addQueryMapQueryParameters(queryMap, template);
}
if (metadata.headerMapIndex() != null) {
template =
addHeaderMapHeaders((Map<String, Object>) argv[metadata.headerMapIndex()], template);
}
return template;
}
下面开始执行请求并处理返回类型
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
// 应用RequestTemplate中的所有interceptor,并且生成Request
Request request = targetRequest(template);
if (logLevel != Logger.Level.NONE) {
logger.logRequest(metadata.configKey(), logLevel, request);
}
Response response;
long start = System.nanoTime();
try {
// 如果没有配置Ribbon, 默认的Client是Default类型,使用HttpURLConnection发送请求与返回。
response = client.execute(request, options);
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
}
throw errorExecuting(request, e);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
boolean shouldClose = true;
try {
if (logLevel != Logger.Level.NONE) {
response =
logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
}
// 方法返回类型是Response类型则直接返回response
if (Response.class == metadata.returnType()) {
if (response.body() == null) {
return response;
}
if (response.body().length() == null ||
response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
shouldClose = false;
return response;
}
// Ensure the response body is disconnected
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
return response.toBuilder().body(bodyData).build();
}
if (response.status() >= 200 && response.status() < 300) {
if (void.class == metadata.returnType()) {
return null;
} else {
Object result = decode(response);
shouldClose = closeAfterDecode;
return result;
}
} else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
Object result = decode(response);
shouldClose = closeAfterDecode;
return result;
} else {
// 调用ErrorDecoder
throw errorDecoder.decode(metadata.configKey(), response);
}
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
}
throw errorReading(request, response, e);
} finally {
if (shouldClose) {
ensureClosed(response.body());
}
}
}
下面插一个时序图方便理解: