基于
spring-cloud-openfeign-core-2.2.5.RELEASE
。
在前文 feign源码解析_初始化中,我们探讨了feign如何基于JDK Proxy特性,构建出相应的@FeignClient接口实现,供使用者取用。
本文我们将以此为基础,探讨在上述@FeignClient实例基础上,feign是如何基于使用者的接口调用请求,发起相应的http调用,返回相应结果的。
ReflectiveFeign.FeignInvocationHandler
如前文 feign源码解析_初始化已经论述的,feign基于JDK Proxy实现的@FeignClient接口实现类。基于此我们找到典型的InvocationHandler
实现类,在本文场景下就是ReflectiveFeign.FeignInvocationHandler
。
// FeignInvocationHandler.invoke(...)
@Override
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();
}
// 最终调用的是 MethodHandler.invoke(Object[] argv), 默认实现类为 SynchronousMethodHandler
return dispatch.get(method).invoke(args);
}
SynchronousMethodHandler.invoke(Object[] argv)
上述FeignInvocationHandler
的实现,只是简单地将逻辑控制权调度给了MethodHandler
实现,这里是SynchronousMethodHandler
。
// ================================== SynchronousMethodHandler.invoke(...)
@Override
public Object invoke(Object[] argv) throws Throwable {
// 基于入参,构建出http request template。
// 1. @FeignClient修饰的接口类, 由 方法参数 生成出 RequestTemplate实例。
RequestTemplate template = buildTemplateFromArgs.create(argv);
// 找出可能的Options配置项。使用者可以在@FeignClient接口方法中定义相应类型参数, 达到灵活配置目的.
Options options = findOptions(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
// 主体逻辑处
return executeAndDecode(template, options);
} catch (RetryableException e) {
// 如果发生retry异常
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;
}
}
}
// ================================== SynchronousMethodHandler.executeAndDecode(...)
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
// 依次回调外界自定义的 RequestInterceptor 实现
// 2. 由 RequestTemplate实例 生成 Request实例
Request request = targetRequest(template);
if (logLevel != Logger.Level.NONE) {
logger.logRequest(metadata.configKey(), logLevel, request);
}
Response response;
// 记录请求耗时
long start = System.nanoTime();
try {
// 3. 使用Client实现类(LoadBalancerFeignClient.java这个对外门面), 发起http请求, 获取响应。
response = client.execute(request, options);
// ensure the request is set. TODO: remove in Feign 12
response = response.toBuilder()
.request(request)
.requestTemplate(template)
.build();
} catch (IOException e) {
// 注意这里捕获的异常类型, 所以如果出现类似错误"com.netflix.client.ClientException: Load balancer does not have available server for client: xx-service"
// 那流程就不会往下走了, 也自然不能触发 ErrorDecoder 扩展机制; 将直接跳进 Fallback
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);
// 调用 Decoder接口实现类, 默认为null
if (decoder != null)
return decoder.decode(response, metadata.returnType());
// 回调 AsyncResponseHandler类方法(详解参见下方专门小节)
// 4. 解析结果
CompletableFuture<Object> resultFuture = new CompletableFuture<>();
// 下方专门小节进行解读
asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,
metadata.returnType(),
elapsedTime);
try {
if (!resultFuture.isDone())
throw new IllegalStateException("Response handling not done");
// 5. 返回结果
return resultFuture.join();
} catch (CompletionException e) {
// 上面的 CompletableFuture 执行失败抛出异常, 逻辑将跳转到这里
Throwable cause = e.getCause();
if (cause != null)
throw cause;
throw e;
}
}
AsyncResponseHandler.handleResponse(...)
feign中使用了专门的内部类 AsyncResponseHandler
来负责解析本次请求的响应Response。
// AsyncResponseHandler.handleResponse(...)
void handleResponse(CompletableFuture<Object> resultFuture,
String configKey,
Response response,
Type returnType,
long elapsedTime) {
// copied fairly liberally from SynchronousMethodHandler
boolean shouldClose = true;
try {
if (logLevel != Level.NONE) {
response = logger.logAndRebufferResponse(configKey, logLevel, response,
elapsedTime);
}
// 1. 如果@FeignClient接口方法定义返回值类型就是Response
if (Response.class == returnType) {
if (response.body() == null) {
resultFuture.complete(response);
} else if (response.body().length() == null
|| response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
shouldClose = false;
resultFuture.complete(response);
} else {
// Ensure the response body is disconnected
final byte[] bodyData = Util.toByteArray(response.body().asInputStream());
resultFuture.complete(response.toBuilder().body(bodyData).build());
}
} else if (response.status() >= 200 && response.status() < 300) {
// 2. 如果http response code 为 2xx
if (isVoidType(returnType)) {
resultFuture.complete(null);
} else {
final Object result = decode(response, returnType);
shouldClose = closeAfterDecode;
resultFuture.complete(result);
}
} else if (decode404 && response.status() == 404 && !isVoidType(returnType)) {
// 3. 如果http response code 为 404, 并且配置了feign会处理404错误.
final Object result = decode(response, returnType);
shouldClose = closeAfterDecode;
resultFuture.complete(result);
} else {
// 4. 认定http请求调用, 发生异常.
resultFuture.completeExceptionally(errorDecoder.decode(configKey, response));
}
} catch (final IOException e) {
if (logLevel != Level.NONE) {
logger.logIOException(configKey, logLevel, e, elapsedTime);
}
resultFuture.completeExceptionally(errorReading(response.request(), response, e));
} catch (final Exception e) {
resultFuture.completeExceptionally(e);
} finally {
if (shouldClose) {
ensureClosed(response.body());
}
}
}
以上处理流程,串起来就是下面这副 feign运行时处理时序图了。