SpringCloud FeignClient+Ribbon 底层实现原理(三)

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);
        }
    }
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值