概述
SpringCloud FeignClient+Ribbon 底层实现原理(三)
说实话上一篇文章确实内容比较少,所用在开一篇文章彻底将Feign和Ribbon怎么结合的讲下在上一篇文章中我们讲到了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);
response = response.toBuilder().request(request).requestTemplate(template).build();
} catch (IOException var13) {
if (this.logLevel != Level.NONE) {
this.logger.logIOException(this.metadata.configKey(), this.logLevel, var13, this.elapsedTime(start));
}
throw FeignException.errorExecuting(request, var13);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
if (this.decoder != null) {
return this.decoder.decode(response, this.metadata.returnType());
} else {
CompletableFuture<Object> resultFuture = new CompletableFuture();
this.asyncResponseHandler.handleResponse(resultFuture, this.metadata.configKey(), response, this.metadata.returnType(), elapsedTime);
try {
if (!resultFuture.isDone()) {
throw new IllegalStateException("Response handling not done");
} else {
return resultFuture.join();
}
} catch (CompletionException var12) {
Throwable cause = var12.getCause();
if (cause != null) {
throw cause;
} else {
throw var12;
}
}
}
}
接着我们分析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);
}
}
最后
以上就是长情时光为你收集整理的SpringCloud FeignClient+Ribbon 底层实现原理(三)的全部内容,希望文章能够帮你解决SpringCloud FeignClient+Ribbon 底层实现原理(三)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复