代码都准备好了,可以开始分析了。

  1. 执行调用

  2. http://localhost:8005/trade/testRibbon/2

为什么这么就能调用到服务提供者的方法?

打断点,可以看到restTemplate里有两个拦截器,根据名字可以推断RetryLoadBalancerInterceptor是关键。

跟踪到RetryLoadBalancerInterceptor类

@Override

public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,

final ClientHttpRequestExecution execution) throws IOException {

final URI originalUri = request.getURI();

//获取到service的name

final String serviceName = originalUri.getHost();

Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);

//根据serviceName和LoadBalancerClient,LoadBalancedRetryPolicy里面包含了RibbonLoadBalancerContext和ServiceInstanceChooser

final LoadBalancedRetryPolicy retryPolicy = lbRetryFactory.createRetryPolicy(serviceName,

loadBalancer);

RetryTemplate template = createRetryTemplate(serviceName, request, retryPolicy);

//执行方法会进入到doExecute方法

return template.execute(context -> {

ServiceInstance serviceInstance = null;

if (context instanceof LoadBalancedRetryContext) {

LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext) context;

serviceInstance = lbContext.getServiceInstance();

}

if (serviceInstance == null) {

serviceInstance = loadBalancer.choose(serviceName);

}

ClientHttpResponse response = RetryLoadBalancerInterceptor.this.loadBalancer.execute(

serviceName, serviceInstance,

requestFactory.createRequest(request, body, execution));

int statusCode = response.getRawStatusCode();

if (retryPolicy != null && retryPolicy.retryableStatusCode(statusCode)) {

byte[] bodyCopy = StreamUtils.copyToByteArray(response.getBody());

response.close();

throw new ClientHttpResponseStatusCodeException(serviceName, response, bodyCopy);

}

return response;

}, new LoadBalancedRecoveryCallback<ClientHttpResponse, ClientHttpResponse>() {

//This is a special case, where both parameters to LoadBalancedRecoveryCallback are

//the same. In most cases they would be different.

@Override

protected ClientHttpResponse createResponse(ClientHttpResponse response, URI uri) {

return response;

}

});

}

doExecute方法:

protected <T, E extends Throwable> T doExecute(RetryCallback<T, E> retryCallback,

RecoveryCallback recoveryCallback, RetryState state)

throws E, ExhaustedRetryException {

//省略部分代码

/*

  • We allow the whole loop to be skipped if the policy or context already

  • forbid the first try. This is used in the case of external retry to allow a

  • recovery in handleRetryExhausted without the callback processing (which

  • would throw an exception).

*/

//执行逻辑的关键方法

while (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) {

}

继续跟踪canRetry方法

@Override

public boolean canRetry(RetryContext context) {

LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext)context;

if(lbContext.getRetryCount() == 0 && lbContext.getServiceInstance() == null) {

//We haven’t even tried to make the request yet so return true so we do

//设置选中的服务提供者

lbContext.setServiceInstance(serviceInstanceChooser.choose(serviceName));

return true;

}

return policy.canRetryNextServer(lbContext);

}

我们跟踪serviceInstanceChooser.choose(serviceName)看看怎么通过serviceName选服务提供者的

@Override

public ServiceInstance choose(String serviceId) {

//选择server

Server server = getServer(serviceId);

if (server == null) {

return null;

}

return new RibbonServer(serviceId, server, isSecure(server, serviceId),

serverIntrospector(serviceId).getMetadata(server));

}

跟踪getServer方法

protected Server getServer(ILoadBalancer loadBalancer) {

if (loadBalancer == null) {

return null;

}

//可以看出是loadBalancer在选择

return loadBalancer.chooseServer(“default”); // TODO: better handling of key

}

继续深入

public Server chooseServer(Object key) {

if (counter == null) {

counter = createCounter();

}

//有一个调用次数在+1

counter.increment();

if (rule == null) {

return null;

} else {

try {

//委托给了IRule,所以Irule是负载均衡的关键,最后来总结

return rule.choose(key);

} catch (Exception e) {

logger.warn(“LoadBalancer [{}]: Error choosing server for key {}”, name, key, e);

return null;

}

}

}

查看Irule的实现

public Server choose(Object key) {

ILoadBalancer lb = getLoadBalancer();

//lb.getAllServers里面是所有的服务提供者列表

Optional server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);

if (server.isPresent()) {

return server.get();

} else {

return null;

}

}

跟踪chooseRoundRobinAfterFiltering方法

public Optional chooseRoundRobinAfterFiltering(List servers, Object loadBalancerKey) {

//拿到筛选后的servers

List eligible = getEligibleServers(servers, loadBalancerKey);

if (eligible.size() == 0) {

return Optional.absent();

}

//incrementAndGetModulo方法拿到下标,然后根据list.get取到一个服务

return Optional.of(eligible.get(incrementAndGetModulo(eligible.size())));

}

至此就拿到了具体的服务提供者。

但是到这里还有个问题?

  • 怎么根据服务名拿到server的?

  • 有一个ServerList接口是用于拿到服务列表的。我们使用的loadBalancer(ZoneAwareLoadBalancer)的父类DynamicServerListLoadBalancer类的构造方法里,有一个restOfinit方法

public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,

ServerList serverList, ServerListFilter filter,

ServerListUpdater serverListUpdater) {

super(clientConfig, rule, ping);

this.serverListImpl = serverList;

this.filter = filter;

this.serverListUpdater = serverListUpdater;

if (filter instanceof AbstractServerListFilter) {

((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats());

}

restOfInit(clientConfig);

}

跟踪restOfInit方法

void restOfInit(IClientConfig clientConfig) {

boolean primeConnection = this.isEnablePrimingConnections();

// turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()

this.setEnablePrimingConnections(false);

enableAndInitLearnNewServersFeature();

//用于获取所有的serverList

updateListOfServers();

if (primeConnection && this.getPrimeConnections() != null) {

this.getPrimeConnections()

.primeConnections(getReachableServers());

}

this.setEnablePrimingConnections(primeConnection);

LOGGER.info(“DynamicServerListLoadBalancer for client {} initialized: {}”, clientConfig.getClientName(), this.toString());

}

继续跟踪updateListOfServers方法

public void updateListOfServers() {

List servers = new ArrayList();

if (serverListImpl != null) {

//查询serverList

servers = serverListImpl.getUpdatedListOfServers();

LOGGER.debug(“List of Servers for {} obtained from Discovery client: {}”,

getIdentifier(), servers);

if (filter != null) {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

即使是面试跳槽,那也是一个学习的过程。只有全面的复习,才能让我们更好的充实自己,武装自己,为自己的面试之路不再坎坷!今天就给大家分享一个Github上全面的Java面试题大全,就是这份面试大全助我拿下大厂Offer,月薪提至30K!

我也是第一时间分享出来给大家,希望可以帮助大家都能去往自己心仪的大厂!为金三银四做准备!
一共有20个知识点专题,分别是:

Dubbo面试专题

JVM面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

Java并发面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

Kafka面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

MongDB面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

MyBatis面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

MySQL面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

Netty面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

RabbitMQ面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

Redis面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

Spring Cloud面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

SpringBoot面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

zookeeper面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

常见面试算法题汇总专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

计算机网络基础专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

设计模式专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
*

[外链图片转存中…(img-C5nzksiq-1712764918618)]

Spring Cloud面试专题

[外链图片转存中…(img-D5DoNiMp-1712764918618)]

SpringBoot面试专题

[外链图片转存中…(img-oCMjCzJm-1712764918618)]

zookeeper面试专题

[外链图片转存中…(img-q0Jd0jxK-1712764918619)]

常见面试算法题汇总专题

[外链图片转存中…(img-KMOhvAqo-1712764918619)]

计算机网络基础专题

[外链图片转存中…(img-oXCBMqbV-1712764918619)]

设计模式专题

[外链图片转存中…(img-IoTmOgea-1712764918619)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

Logo

欢迎加入西安开发者社区!我们致力于为西安地区的开发者提供学习、合作和成长的机会。参与我们的活动,与专家分享最新技术趋势,解决挑战,探索创新。加入我们,共同打造技术社区!

更多推荐