所谓一图胜千言,本篇文章先用Spring Cloud Feign类的脑图勾勒一个对Feign的简明印象。然后通过http请求的流程图和源码解析来解读Feign的功能。下图是对Spring Cloud Feign类的一个简单描述,先对整体的框架有个了解。
下图可以看出OpenFeign在微服务架构中的角色,即方便构建网络请求应用。
下图为调用OpenFeign的Http请求流程图
(图片来自feign初識及源碼分析)
(图片来自feign初識及源碼分析)
OpenFeign的网络请求流程在源码里是如何的呢,下面将从类ReflectiveFeign开始展开
ReflectiveFeign$FeignInvocationHandler
private final Map dispatch;
@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来处理, 即交给SynchronousMethodHandler来处理
return dispatch.get(method).invoke(args);
}
dispatch是个容器类,通过调用MethodHandler.invoke(args)方法来处理,此处调用的是SynchronousMethodHandler的invoke()方法,该方法1生成传入实际参数值的RequestTemplate,2然后调用Target生成具体的Request对象(此处在生成Request对象前,会调用所有RequestInterceptor对RequestTemplate进行处理),3最后调用Client#execute()方法来发送网络请求
1. 生成RequestTemplate
SynchronousMethodHandler
@Override
public Object invoke(Object[] argv) throws Throwable {
// 将函数实际参数添加到RequesstTemplate,使用类ReflectiveFeign$BuildTemplateByResolvingArgs的create()方法
RequestTemplate template = buildTemplateFromArgs.create(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
// 根据RequestTemplate生成request对象,然后交给Client实例发送网络请求
return executeAndDecode(template);
} catch (RetryableException e) {
try {
// 如果Response#status()为503,即重试请求,调用该方法重试,如果超过最大重试次数maxAttempts,抛出异常
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;
}
}
}
ReflectiveFeign$BuildTemplateByResolvingArgs
@Override
public RequestTemplate create(Object[] argv) {
RequestTemplate mutable = RequestTemplate.from(metadata.template());
if (metadata.urlIndex() != null) {
// 设置URL
int urlIndex = metadata.urlIndex();
checkArgument(argv[urlIndex] != null, "URI parameter %s was null", urlIndex);
mutable.target(String.valueOf(argv[urlIndex]));
}
Map varBuilder = new LinkedHashMap();
// 遍历MethodMetaData中所有关于参数的索引及其对应名称的配置信息
for (Entry> entry : metadata.indexToName().entrySet()) {
int i = entry.getKey();
// entry.getKey就是参数的索引
Object value = argv[entry.getKey()];
if (value != null) { // Null values are skipped.
// indexToExpander保存着将各种类型参数的值转换为string类型的Expander转换器
if (indexToExpander.containsKey(i)) {
// 将value值转换为String
value = expandElements(indexToExpander.get(i), value);
}
for (String name : entry.getValue()) {
varBuilder.put(name, value);
}
}
}
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()];
// 设置queryMap参数
Map queryMap = toQueryMap(value);
template = addQueryMapQueryParameters(queryMap, template);
}
if (metadata.headerMapIndex() != null) {
// 设置headerMap参数
template =
addHeaderMapHeaders((Map) argv[metadata.headerMapIndex()], template);
}
return template;
}
2.调用Target生成具体的Request对象
SynchronousMethodHandler
Object executeAndDecode(RequestTemplate template) throws Throwable {
// 调用targetRequest()方法,构建Request
Request request = targetRequest(template);
if (logLevel != Logger.Level.NONE) {
logger.logRequest(metadata.configKey(), logLevel, request);
}
Response response;
long start = System.nanoTime();
try {
// 调用Client.execute()方法发送网络请求
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) {
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 {
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());
}
}
}
SynchronousMethodHandler
Request targetRequest(RequestTemplate template) {
// 调用所有的请求拦截器,为每个请求添加固定的header信息
for (RequestInterceptor interceptor : requestInterceptors) {
interceptor.apply(template);
}
return target.apply(template);
}
Target
// 创建Request
@Override
public Request apply(RequestTemplate input) {
if (input.url().indexOf("http") != 0) {
input.target(url());
}
return input.request();
}
3. 调用Client#execute()方法来发送网络请求
此处以OkHttp的方法举例,OkHttpClient
@Override
public feign.Response execute(feign.Request input, feign.Request.Options options)
throws IOException {
okhttp3.OkHttpClient requestScoped;
if (delegate.connectTimeoutMillis() != options.connectTimeoutMillis()
|| delegate.readTimeoutMillis() != options.readTimeoutMillis()) {
requestScoped = delegate.newBuilder()
.connectTimeout(options.connectTimeoutMillis(), TimeUnit.MILLISECONDS)
.readTimeout(options.readTimeoutMillis(), TimeUnit.MILLISECONDS)
.followRedirects(options.isFollowRedirects())
.build();
} else {
requestScoped = delegate;
}
// 将feign.Request转换为OkHttp的Request对象
Request request = toOkHttpRequest(input);
// 使用OkHttp的同步操作发送网络请求
Response response = requestScoped.newCall(request).execute();
// 将OkHttp的Response转换为feign.Response
return toFeignResponse(response, input).toBuilder().request(input).build();
}
推荐阅读
Spring-cloud-openFeign source depth analysis
Spring Cloud Feign的loadbalancerkey扩展
Spring Cloud Issue - public FeignLoadBalancer.RibbonRequest and FeignLoadBalancer.RibbonResponse to the user to customize their needs
Spring Cloud Netflix: Load Balancer with Ribbon/Feign
Spring Cloud Issue - Unable to build or run all tests successfully on windows
feign初識及源碼分析
spring-cloud-openfeign原理分析