手撕SpringCloud负载均衡源码 ,深入了解Spring Cloud LoadBalancer

有兴趣可以关注下我的个人公众号哦

由于最近使用了目前最新的 Springboot 2.5.0-RC1,在整合 SpringCloud 2020.0.3 版本 过程中,发现原先使用的ribbon竟然无法使用,经过测试,负载均衡的功能还可以正常使用,好奇心驱使下,将最新版的SpringCloud负载均衡器(Spring Cloud LoadBalancer)这块源码深入的读了下,并进行画图和笔记记录,今天就分享给大家,这个版本目前网上没有的资料,希望给个好评哟!

 

废话不多说,直接上干货,先上一张完整的 Spring Cloud LoadBalancer 源码分析流程图(高清,可放大) 奉上:

 

如果各位仔细看了上面我画的这张图,基本上脑海里面已经有大概的概念了,下面我们就来详细的用文字描述下上述的流程图。

 

1、案例背景:

两个服务注册中心,三个微服务系统,

其中 eureka-client-a 需要调用 eureka-client-b 的接口

如图:

 

 

2、eureka-client-a 请求代码

@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
    return new RestTemplate();
}

@GetMapping(value = "/greeting/{msg}")
public String greeting(@PathVariable("msg") String mgs) {
    RestTemplate restTemplate = getRestTemplate();
    return restTemplate.getForObject("http://eureka-client-b/hello/" + mgs, String.class);
}

从上述代码中很明显能判断出来 @LoadBalanced 这个注解将一个RestTemplate标志为底层采用LoadBalancerClient来执行实际的http请求,支持负载均衡。

 

3、Spring Cloud LoadBalancer 全文解析:

首先是加载了 LoadBalancerAutoConfiguration.class 文件,那么我们来阅读一下这个类,首先看到的方法就是 loadBalancedRestTemplateInitializerDeprecated,实例化出来了一个SmartInitializingSingleton,一看就知道是在spring singleton bean实例化完了之后来执行的一个Bean加载。

看源码拿到当前的RestTemplate集合,遍历。随后又对每个RestTemplate,又遍历一个Cutomizer(专门用来定制化RestTemplate的组件)。用每个Customizer来定制每个RestTemplate。

好,看到这至少明白一件事,那么就是RestTemplate已经被SpringCloud定制化了

继续往下看,这个定制化的RestTemplate是什么呢?

List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);

看源码很轻松的就知道,在这里为每个RestTemplate加载了一个拦截器,嗯,这块应该就是核心逻辑了,在我们脑海中目前应该是这么觉得的:在执行RestTemplate发送物理请求之前,SpringCloud对这个请求进行了拦截,并做了相应的逻辑处理。

好,那咱们在来看看这个拦截器具体做了些什么。

费尽周折,终于找到这个拦截器的实例对象 BlockingLoadBalancerClient.class(我在这懵了一段时间,就是因为之前这边的实例对象是RibbonLoadBalancerClient,用的都是Ribbon的组件,刚开始我还以为不用手动导入一个ribbon-start的包了,是因为最新版的SpringCloud整合进去了,后来源码一读才发现自己错的离谱,技术真的更新的好快呀!!!)

这块代码就是核心中的核心,我把他全部贴出来,仔细分析下,拦截器啥的就不用我多说了吧,直接找到这个类的execute方法,该方法主要做的事情如下:

    a. 执行,ServiceInstance serviceInstance = this.choose(serviceId, lbRequest);方法,根据初始化时得到的LoadBalancerClientFactory获取了一个负载平衡器,从注册表中匹配到对应的服务名,获取服务名下的Instance实例集合,在根据获取到的负载平衡器的算法,挑选一个服务器进行发送请求。

// 获取 负载平衡器
ReactiveLoadBalancer<ServiceInstance> loadBalancer = this.loadBalancerClientFactory.getInstance(serviceId);
if (loadBalancer == null) {
    return null;
} else {
    // 根据 获取到的负载均衡器算法,获取一个请求的服务实例
    Response<ServiceInstance> loadBalancerResponse = (Response)Mono.from(loadBalancer.choose(request)).block();
    return loadBalancerResponse == null ? null : (ServiceInstance)loadBalancerResponse.getServer();
}

    b. 根据获取到的服务实例,发送真正的物理请求

    其实这块就没什么好说的了,无非就是将获取到的请求,找一个Http的组件发送一个物理请求,获得数据而已,嗯,这里可以补充的一个知识点就是,SpringCloud LoadBalancer发送的请求时使用Http协议的,在说具体点,使用的就是spring-web 5.3.6包下的InterceptingClientHttpRequest.class发送的请求,至此所有流程结束。

 

 

最后在给大家梳理一下,

1、首先是SpringCloud使用了一个拦截器,对发送的所有请求进行了定制化的需求改动

2、核心的代码是在spring-cloud-loadbalancer.jar中的BlockingLoadBalancerClient.class

3、目前SpringCloud Load Balancer只支持两种负载策略

1)RandomLoadBalancer:随机找一个服务器

2)RoundRobinLoadBalancer:系统内置的默认负载均衡规范,直接round robin轮询,从一堆server list中,不断的轮询选择出来一个server,每个server平摊到的这个请求,基本上是平均的

4、很多人问我,目前SpringCloudLoadBalancer是不是有点弱,确实ribbon已经很成熟了,支持超时、懒加载处理、重试及其和 hystrix整合高级属性等,而SpringCloudLoadBalancer目前负载策略都只有两个,为什么还要进行升级,老版的他不香吗?

其实我觉得这个问题还是一个取舍的问题,咱们做Java开发的另一个名字是什么?Spring开发工程师啊。

Spring官方从19年就开始强调要下架Netflix了,而且原因在上一篇中已经说的很清楚,你不跟着Spring的大脚步往前走,还继续停留在好几年前的版本上?

作为一个技术人员,别说好几年前,就是1年,也足够你落伍了,这也是为什么很多行业大佬都是专精一个或者两个技术的原因,专家就是这么来的,没有多余的精力给你全面发展,一定要明白自身的优势是什么,并长期为之学习和发展下去。

所以不管怎么样,还是得去尝试这次巨大的更新,当然,我也理解很多线上技术不好替换,但是不妨碍你先研究研究,等Spring这套体系在完善一些,就可以直接替换,不需要临时的在做技术调研了。

好了,今天就讲到这吧,感谢各位  ^ _ ^

 

 

 

 

 

 

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值