共计 4130 个字符,预计需要花费 11 分钟才能阅读完成。
本系列代码地址:https://github.com/JoJoTec/sp…
接下来,咱们开始剖析 OpenFeign 同步环境下的生命周期的第二局部,应用 SynchronousMethodHandler 进行理论调用,其流程能够总结为:
- 调用代理类的办法理论调用的是后面一章中生成的
InvocationHandler
的invoke
办法。 - 默认实现是查问
Map<Method, MethodHandler> methodToHandler
找到对应的MethodHandler
进行调用,对于同步 Feign,其实就是SynchronousMethodHandler
-
对于
SynchronousMethodHandler
:- 应用后面一章剖析创立的创立的申请模板工厂
RequestTemplate.Factory
,创立申请模板RequestTemplate
。 - 读取 Options 配置
- 应用配置的 Retryer 创立新的 Retryer
- 执行申请并将响应反序列化 – executeAndDecode:
- 如果配置了 RequestInterceptor,则执行每一个 RequestInterceptor
- 将申请模板 RequestTemplate 转化为理论申请 Request
- 通过 Client 执行 Request
- 如果响应码是 2XX,应用 Decoder 解析 Response
- 如果响应码是 404,并且在后面一章介绍的配置中配置了 decode404 为 true,应用 Decoder 解析 Response
- 对于其余响应码,应用 errorDecoder 解析,能够本人实现 errorDecoder 抛出 RetryableException 来走入重试逻辑
-
如果以上步骤抛出 IOException,间接封装成 RetryableException 抛出
- 如果第 4 步抛出 RetryableException,则应用第三步创立的 Retryer 判断是否重试,如果须要重试,则从新走第 4 步,否则,抛出异样。
- 应用后面一章剖析创立的创立的申请模板工厂
给出这个流程后,咱们来详细分析
OpenFeign 的生命周期 - 进行调用源码剖析
后面一章的最初,咱们曾经从源码中看到了这一章结尾提到的流程的前两步,咱们间接从第三步开始剖析。
SynchronousMethodHandler
public Object invoke(Object[] argv) throws Throwable {
// 应用后面一章剖析创立的创立的申请模板工厂 `RequestTemplate.Factory`,创立申请模板 `RequestTemplate`。RequestTemplate template = buildTemplateFromArgs.create(argv);
// 读取 Options 配置
Options options = findOptions(argv);
// 应用配置的 Retryer 创立新的 Retryer
Retryer retryer = this.retryer.clone();
while (true) {
try {
// 执行申请并将响应反序列化
return executeAndDecode(template, options);
} catch (RetryableException e) {
// 如果抛出 RetryableException,则应用 retryer 判断是否重试,如果须要重试,则持续申请即重试,否则,抛出异样。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;
}
}
}
对于 executeAndDecode 其中的源码,为了兼容异步 OpenFeign 兼容 CompletableFuture 的个性,做了一些兼容性批改导致代码比拟难以了解,因为咱们这里不关怀异步 Feign,所以咱们将这块代码还原回来,在这里展现:
这个批改对应的 Issue 和 PullRequest 是:
- Non-blocking I/O support
- issue 361 – async feign variant supporting CompleteableFutures
Request targetRequest(RequestTemplate template) {
// 如果配置了 RequestInterceptor,则执行每一个 RequestInterceptor
for (RequestInterceptor interceptor : requestInterceptors) {interceptor.apply(template);
}
// 将申请模板 RequestTemplate 转化为理论申请 Request
return target.apply(template);
}
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {Request request = targetRequest(template);
if (logLevel != Logger.Level.NONE) {logger.logRequest(metadata.configKey(), logLevel, request);
}
Response response;
long start = System.nanoTime();
try {
// 通过 Client 执行 Request
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) {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);
}
// 如果响应码是 2XX,应用 Decoder 解析 Response
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()) {
// 如果响应码是 404,并且在后面一章介绍的配置中配置了 decode404 为 true,应用 Decoder 解析 Response
Object result = decode(response);
shouldClose = closeAfterDecode;
return result;
} else {
// 对于其余响应码,应用 errorDecoder 解析,能够本人实现 errorDecoder 抛出 RetryableException 来走入重试逻辑
throw errorDecoder.decode(metadata.configKey(), response);
}
} catch (IOException e) {if (logLevel != Logger.Level.NONE) {logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
}
// 如果抛出 IOException,间接封装成 RetryableException 抛出
throw errorReading(request, response, e);
} finally {if (shouldClose) {ensureClosed(response.body());
}
}
}
static FeignException errorReading(Request request, Response response, IOException cause) {
return new FeignException(response.status(),
format("%s reading %s %s", cause.getMessage(), request.httpMethod(), request.url()),
request,
cause,
request.body(),
request.headers());
}
这样,咱们就剖析完 OpenFeign 的生命周期
咱们这一节具体介绍了 OpenFeign 进行调用的具体流程。接下来咱们将开始介绍,spring-cloud-openfeign 外面,是如何定制 OpenFeign 的组件并粘合的。
微信搜寻“我的编程喵”关注公众号,每日一刷,轻松晋升技术,斩获各种 offer:
正文完
发表至: spring-cloud
2021-10-17