SpringCloud FeignClient+Ribbon 底层实现原理(二)
这一篇主要讲解在调用feign之后的动态代理invoke进行了什么操作
在上一篇文章中我们讲到了jdk的动态代理,我们重点看下invocationHanlder类SynchronousMethodHandler
主要分析invoke方法和executeAndDecode,invoke方法是代理类调用方法的入口,通过该方法调用executeAndDecode方法,executeAndDecode方法中真正实现调用的的code为,其中client为LoadBalancerFeignClient,调用带有负载均衡的client请求了服务提供者
response = this.client.execute(request, options);
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = this.buildTemplateFromArgs.create(argv);
Options options = this.findOptions(argv);
Retryer retryer = this.retryer.clone();
while(true) {
try {
return this.executeAndDecode(template, options);
} catch (RetryableException var9) {
RetryableException e = var9;
try {
retryer.continueOrPropagate(e);
} catch (RetryableException var8) {
Throwable cause = var8.getCause();
if (this.propagationPolicy == ExceptionPropagationPolicy.UNWRAP && cause != null) {
throw cause;
}
throw var8;
}
if (this.logLevel != Level.NONE) {
this.logger.logRetry(this.metadata.configKey(), this.logLevel);
}
}
}
}
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
Request request = this.targetRequest(template);
if (this.logLevel != Level.NONE) {
this.logger.logRequest(this.metadata.configKey(), this.logLevel, request);
}
long start = System.nanoTime();
Response response;
try {
response = this.client.execute(request, options);
} catch (IOException var16) {
if (this.logLevel != Level.NONE) {
this.logger.logIOException(this.metadata.configKey(), this.logLevel, var16, this.elapsedTime(start));
}
throw FeignException.errorExecuting(request, var16);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
boolean shouldClose = true;
try {
if (this.logLevel != Level.NONE) {
response = this.logger.logAndRebufferResponse(this.metadata.configKey(), this.logLevel, response, elapsedTime);
}
if (Response.class == this.metadata.returnType()) {
Response var19;
if (response.body() == null) {
var19 = response;
return var19;
} else if (response.body().length() != null && (long)response.body().length() <= 8192L) {
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
Response var21 = response.toBuilder().body(bodyData).build();
return var21;
} else {
shouldClose = false;
var19 = response;
return var19;
}
} else {
Object result;
Object var11;
if (response.status() >= 200 && response.status() < 300) {
if (Void.TYPE == this.metadata.returnType()) {
result = null;
return result;
} else {
result = this.decode(response);
shouldClose = this.closeAfterDecode;
var11 = result;
return var11;
}
} else if (this.decode404 && response.status() == 404 && Void.TYPE != this.metadata.returnType()) {
result = this.decode(response);
shouldClose = this.closeAfterDecode;
var11 = result;
return var11;
} else {
throw this.errorDecoder.decode(this.metadata.configKey(), response);
}
}
} catch (IOException var17) {
if (this.logLevel != Level.NONE) {
this.logger.logIOException(this.metadata.configKey(), this.logLevel, var17, elapsedTime);
}
throw FeignException.errorReading(request, response, var17);
} finally {
if (shouldClose) {
Util.ensureClosed(response.body());
}
}
}
接着我们分析LoadBalancerFeignClient的execute方法,看看负载均衡的逻辑是如何实现的,首先看下RibbonRequest的构造,发现this.delegate这个参数其实就是真正运行client的引用如果你配置OkHttpClient那么此处就是OkHttpClient,剩下两个参数就是请求参数和只带有服务名的url
FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(this.delegate, request, uriWithoutHost);
构造完RibbonRequst后接下来就开始进行负载均衡请求了
lbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
@Override
public Response execute(Request request, Request.Options options) throws IOException {
try {
URI asUri = URI.create(request.url());
String clientName = asUri.getHost();
URI uriWithoutHost = cleanUrl(request.url(), clientName);
FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
this.delegate, request, uriWithoutHost);
IClientConfig requestConfig = getClientConfig(options, clientName);
return lbClient(clientName)
.executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
}
catch (ClientException e) {
IOException io = findIOException(e);
if (io != null) {
throw io;
}
throw new RuntimeException(e);
}
}
executeWithLoadBalancer方法在AbstractLoadBalancerAwareClient一个抽象类中,executeWithLoadBalancer中通过一个命令模式进行请求,重点分析下command中几个重要方法
public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
LoadBalancerCommand command = this.buildLoadBalancerCommand(request, requestConfig);
try {
return (IResponse)command.submit(new ServerOperation<T>() {
public Observable<T> call(Server server) {
URI finalUri = AbstractLoadBalancerAwareClient.this.reconstructURIWithServer(server, request.getUri());
ClientRequest requestForServer = request.replaceUri(finalUri);
try {
return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
} catch (Exception var5) {
return Observable.error(var5);
}
}
}).toBlocking().single();
} catch (Exception var6) {
Throwable t = var6.getCause();
if (t instanceof ClientException) {
throw (ClientException)t;
} else {
throw new ClientException(var6);
}
}
}
第一个方法就是selectServer方法,该方法将获取server的信息然后进行负载均衡算法选择满足条件的server
private Observable<Server> selectServer() {
return Observable.create(new OnSubscribe<Server>() {
public void call(Subscriber<? super Server> next) {
try {
Server server = LoadBalancerCommand.this.loadBalancerContext.getServerFromLoadBalancer(LoadBalancerCommand.this.loadBalancerURI, LoadBalancerCommand.this.loadBalancerKey);
next.onNext(server);
next.onCompleted();
} catch (Exception var3) {
next.onError(var3);
}
}
});
}
第二个方法是getServerFromLoadBalancer中通过getLoadBalancer方法来获取带有Ribbon规则的负载均衡,前提是你配置了ribbon否则默认为ZoneAvoidanceZule,至此主要流程已经分析完成,具体细节可以debug调试分析具体逻辑
ILoadBalancer lb = this.getLoadBalancer();
public Server getServerFromLoadBalancer(@Nullable URI original, @Nullable Object loadBalancerKey) throws ClientException {
String host = null;
int port = -1;
if (original != null) {
host = original.getHost();
}
if (original != null) {
Pair<String, Integer> schemeAndPort = this.deriveSchemeAndPortFromPartialUri(original);
port = (Integer)schemeAndPort.second();
}
ILoadBalancer lb = this.getLoadBalancer();
if (host == null) {
if (lb != null) {
Server svc = lb.chooseServer(loadBalancerKey);
if (svc == null) {
throw new ClientException(ErrorType.GENERAL, "Load balancer does not have available server for client: " + this.clientName);
}
host = svc.getHost();
if (host == null) {
throw new ClientException(ErrorType.GENERAL, "Invalid Server for :" + svc);
}
logger.debug("{} using LB returned Server: {} for request {}", new Object[]{this.clientName, svc, original});
return svc;
}
if (this.vipAddresses != null && this.vipAddresses.contains(",")) {
throw new ClientException(ErrorType.GENERAL, "Method is invoked for client " + this.clientName + " with partial URI of (" + original + ") with no load balancer configured. Also, there are multiple vipAddresses and hence no vip address can be chosen to complete this partial uri");
}
if (this.vipAddresses == null) {
throw new ClientException(ErrorType.GENERAL, this.clientName + " has no LoadBalancer registered and passed in a partial URL request (with no host:port). Also has no vipAddress registered");
}
try {
Pair<String, Integer> hostAndPort = this.deriveHostAndPortFromVipAddress(this.vipAddresses);
host = (String)hostAndPort.first();
port = (Integer)hostAndPort.second();
} catch (URISyntaxException var8) {
throw new ClientException(ErrorType.GENERAL, "Method is invoked for client " + this.clientName + " with partial URI of (" + original + ") with no load balancer configured. Also, the configured/registered vipAddress is unparseable (to determine host and port)");
}
} else {
boolean shouldInterpretAsVip = false;
if (lb != null) {
shouldInterpretAsVip = this.isVipRecognized(original.getAuthority());
}
if (shouldInterpretAsVip) {
Server svc = lb.chooseServer(loadBalancerKey);
if (svc != null) {
host = svc.getHost();
if (host == null) {
throw new ClientException(ErrorType.GENERAL, "Invalid Server for :" + svc);
}
logger.debug("using LB returned Server: {} for request: {}", svc, original);
return svc;
}
logger.debug("{}:{} assumed to be a valid VIP address or exists in the DNS", host, port);
} else {
logger.debug("Using full URL passed in by caller (not using load balancer): {}", original);
}
}
if (host == null) {
throw new ClientException(ErrorType.GENERAL, "Request contains no HOST to talk to");
} else {
return new Server(host, port);
}
}