Feign的调用流程
Feign的调用流程
动态代理的入口
前面已经分析过了创建的代理是FeignInvocationHandler,那我们就打断点,停在它的反射方法上,看看到底做了什么。
@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();
}
return dispatch.get(method).invoke(args);
}
dispatch此处就是之前封装的5个SynchronousMethodHandler方法的集合,这里更加方法去获取,然后调用invoke方法。来到了SynchronousMethodHandler这个方法。
@Override
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Options options = findOptions(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
return executeAndDecode(template, options);
} catch (RetryableException e) {
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;
}
}
}
第一行首先会创建出一个template的,它的结果如下图:
最终把上面获取到的2个变量带到了executeAndDecode方法,这个方法才是执行和解码的方法。
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
//这里的request就已经把服务名给加上了,变成了一个具体的请求。
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);
// 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);
if (decoder != null)
return decoder.decode(response, metadata.returnType());
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");
return resultFuture.join();
} catch (CompletionException e) {
Throwable cause = e.getCause();
if (cause != null)
throw cause;
throw e;
}
}
Feign是如何实现负载均衡的
先进入到前面的lbClient方法,返回一个FeignLoadBalancer,说明这里和ribbon结合了。
private FeignLoadBalancer lbClient(String clientName) {
return this.lbClientFactory.create(clientName);
}
此处调用了一个create方法,那就进去看看。
注意这里的factory,其实就是SpringClientFactory,从它里面获取了lb,lb里面包含了注册的服务清单,然后再把它放到本地的缓存当中。
接着就会执行,现在它的sumbit方法中打一个断点:
发现里面会执行一个selectServer()方法,肯定是这个里面选择了服务
在点进去看看,于是就找到了ribbon中熟悉的方法了,就是这里选择了那个服务
选择好了那个服务,就继续放下走,开始这边拼接ip和url了
会把选择的ip和后面请求的url都传进来。
在其父类的reconstructURIWithServer方法中完成了拼接,如下图:
后面就是请求响应和解码了