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

执行调用

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() {

//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 doExecute(RetryCallback 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开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

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

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

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

Spring全套教学资料

Spring是Java程序员的《葵花宝典》,其中提供的各种大招,能简化我们的开发,大大提升开发效率!目前99%的公司使用了Spring,大家可以去各大招聘网站看一下,Spring算是必备技能,所以一定要掌握。

目录:

部分内容:

Spring源码

第一部分 Spring 概述第二部分 核心思想第三部分 手写实现 IoC 和 AOP(自定义Spring框架)第四部分 Spring IOC 高级应用 基础特性 高级特性第五部分 Spring IOC源码深度剖析 设计优雅 设计模式 注意:原则、方法和技巧第六部分 Spring AOP 应用 声明事务控制第七部分 Spring AOP源码深度剖析 必要的笔记、必要的图、通俗易懂的语言化解知识难点

脚手框架:SpringBoot技术

它的目标是简化Spring应用和服务的创建、开发与部署,简化了配置文件,使用嵌入式web服务器,含有诸多开箱即用的微服务功能,可以和spring cloud联合部署。

Spring Boot的核心思想是约定大于配置,应用只需要很少的配置即可,简化了应用开发模式。

SpringBoot入门配置文件日志Web开发DockerSpringBoot与数据访问启动配置原理自定义starter

微服务架构:Spring Cloud Alibaba

同 Spring Cloud 一样,Spring Cloud Alibaba 也是一套微服务解决方案,包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。

微服务架构介绍Spring Cloud Alibaba介绍微服务环境搭建服务治理服务容错服务网关链路追踪ZipKin集成及数据持久化消息驱动短信服务Nacos Confifig—服务配置Seata—分布式事务Dubbo—rpc通信

Spring MVC

目录:

部分内容:

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取! 错

服务网关链路追踪ZipKin集成及数据持久化消息驱动短信服务Nacos Confifig—服务配置Seata—分布式事务Dubbo—rpc通信

[外链图片转存中…(img-pDcrXjw7-1711890809591)]

[外链图片转存中…(img-B9l1Tj9b-1711890809591)]

Spring MVC

目录:

[外链图片转存中…(img-Ruloa4hw-1711890809591)]

[外链图片转存中…(img-fUqrZEe3-1711890809591)]

[外链图片转存中…(img-MK7OMhmT-1711890809592)]

部分内容:

[外链图片转存中…(img-9KAqrxWQ-1711890809592)]

[外链图片转存中…(img-huuPnIVN-1711890809592)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!

好文链接

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。