需求
在项目中,经常有基于Restful格式的接口需要调用,特别是远程调用。做法有多种,例如:自己手写http请求接口、使用Spring的RestTemplate进行远程调用等。得益于SpringCloud组件的Feign组件,有了一种易于上手,忽略请求细节的选择方案。
我们当前有很多应用是基于SpringMVC开发的,而目前线上大部分集成Feign的解决方案都是基于SpringCloud,至少基于SpringBoot的场景。(参考网上的案例,自己整理了可行方案)
源码分析
【源码】SynchronousMethodHandler、Client类(含解释)
每次通过业务代码调用接口的时候,都会调用:
/*
* Copyright 2014 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package feign;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;
import feign.InvocationHandlerFactory.MethodHandler;
import feign.Request.Options;
import feign.codec.DecodeException;
import feign.codec.Decoder;
import feign.codec.ErrorDecoder;
import static feign.FeignException.errorExecuting;
import static feign.FeignException.errorReading;
import static feign.Util.checkNotNull;
import static feign.Util.ensureClosed;
final class SynchronousMethodHandler implements MethodHandler {
private static final long MAX_RESPONSE_BUFFER_SIZE = 8192L;
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;
//创建实例,同时根据容器配置的参数进行加载,此处可以在Register(后面有代码)中个性化配置
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) {
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;
}
//方法调用时执行的代码,其中executeAndDecode是最重要的,就是基于http的请求以及数据格式转码
@Override
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
return executeAndDecode(template);
} catch (RetryableException e) {
retryer.continueOrPropagate(e);
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
//源码请求发送和接收处理类
Object executeAndDecode(RequestTemplate template) throws Throwable {
Request request = targetRequest(template);
if (logLevel != Logger.Level.NONE) {
logger.logRequest(metadata.configKey(), logLevel, request);
}
Response response;
long start = System.nanoTime();
try {
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);
}
if (Response.class == metadata.returnType()) {
if (response.body() == null) {