本系列代码地址: https://github.com/JoJoTec/spring-cloud-parent
接下来,我们开始分析 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的生命周期-进行调用源码分析
前面一章的最后,我们已经从源码中看到了这一章开头提到的流程的前两步,我们直接从第三步开始分析。
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 是:
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 的组件并粘合的。