持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第31天,点击查看活动详情
前言
ribbon是Spring cloud Netflix中的一个组件,这个组件是在微服务请求中进行负载均衡的,其负载均衡的实现是通过@LoadBalanced注解进行使用的。简单介绍下这个注解的作用。
负载均衡入口
直接到RestTemplate中看下。请求的发送主要是通过RestTemplate组件的getForObject(...)⽅法发起的,那⾸当其冲的我们肯定先到这个⽅法⾥看下了
接着会进⼊到RestTemplate的getForObject源码中,如下图所示:
我们继续看doExcute⽅法到底⼲了什么。
要是直接闯到RestTemplate的getForObject(...)⽅法中、⼀层⼀层跟下去发现它就是指定 了下请求的类型、请求的回调⽅法、响应的数据的解析器等,这些和我们想要看的Ribbon底层负载均衡 相关的逻辑完全不在⼀个频道上;那现在我们只能从其他的⻆度去出发了,⽽除了RestTemplate组件 外,现在只剩@LoadBalanced注解这⼀个线索了。
查看@LoadBalanced注解
那就到@LoadBalanced注解中看下吧,如下图所示:
很可惜的是,在@LoadBalanced中也没看到什么端倪,但是注释中好像有点提示,翻译⼀下:注解是⽤来配置RestTemplate这样⼀个Bean,然后去调⽤LoadBalancerClient。
根据注释好像是有点眉⽬了,因为LoadBalancerClient简单翻译下不就是负载均衡器的客户端吗,负载 均衡相关的核⼼逻辑应该就在LoadBalancerClient这个接⼝的实现类中,⽽LoadBalancerClient是要通 过RestTemplate作为⼊⼝进去的,但在Demo中对于LoadBalancerClient却没有什么提示信息,所以该 去从哪⾥去找配置RestTemplate的地⽅呢?
查看springcloud相关jar包
现在我们只能去SpringCloud依赖的那些jar包中去找了,但是找也不是乱找的,毕竟我们是在看 SpringCloud整合Ribbon实现负载均衡的逻辑,所以⾸先看下SpringCloud相关的包中含有Ribbon线索的 那些包有哪些,找了⼀下发现和SpringCloud以及Ribbon有点联系的有以下⼏个jar包:
但是打开看了下也没有发现⾥⾯有可靠⼀点的信息,勉强看了下pom⽂件中有⼀些Ribbon相关的依赖, 但是它们都是Ribbon原⽣类的信息,⽽我们这⾥要找的是和SpringCloud整合Ribbon的信息,毕竟原⽣ 的Ribbon可并没有提供注解@LoadBalanced啊。
那会不会是有些SpringCloud相关的jar包,可能名字不是那么明显、但是依然是包含Ribbon相关的逻辑的,只是恰好被我们忽略了呢?顺着这个思路⼜去翻了下SpringCloud相关的jar包,发现springcloud-commons这个jar有点嫌疑,毕竟commons是⼀些公共相关的类,难免Ribbon这⾥也会有⼀些公共的类 被抽到这个包⾥了,进⼊包中看下:
看到这⾥真的是⼀阵窃喜,果然想的没错,这⾥还真的有负载均衡相关的线索:发现了⼀个loadbalancer的包名,进到包下发现类还挺多的,其中有两个类都是以Configuration结尾的,分别是:
- AsyncLoadBalancerAutoConfiguration
- LoadBalancerAutoConfiguration
虽然我们不知道他们都是什么类、实现了什么功能,但是AsyncLoadBalancerAutoConfiguration相⽐于LoadBalancerAutoConfiguration⽽⾔肯定是添加了⼀些Async异步负载均衡相关的功能,算是⼀种⾼阶的特性了,⽽⼀般⼀些⾼阶特性是不常⽤的,所以我们优先选择分析LoadBalancerAutoConfiguration。
进到LoadBalancerAutoConfiguration中,如下图所示:
果然发现了⼀些关键的信息:@LoadBalanced、RestTemplate集合,这不就是我们现在苦苦寻找的东⻄吗,答案应该就在这附近了,可能注解底层转换的⼀些细枝末节我们暂时还不知道,但是直觉告诉我你 这⾥搞个List<RestTemplate,是不是说@LoadBalanced标记⽅法返回的RestTemplate、都会被放到 这⾥来呢?
带着这个猜想,继续看下LoadBalancerAutoConfiguration中还有哪些关键的操作,如下图:
简单往下看了下,发现loadBalanceRestTemplateInitializer(...)⽅法,这个⽅法我们根据⽅法名称可以猜到,应该就是初始化RestTemplate这个组件的。
⽅法中我们看到是遍历restTemplates、并且为每个RestTemplate定制化设置了⼀些东⻄,⽽通过⽅法的提示,可以看到它这⾥要为RestTemplate定制设置三样东⻄,虽然这三个东⻄我们可以还看不太懂到底在⼲些什么,但是其中的LoadBalancerInterceptorConfig不就是拦截器嘛,拦截器这个我们熟悉啊, ⼀般都会在这⾥⾯⼲⼀些⽐较重要的事情,况且这个拦截器的名称还是LoadBalancerInterceptor负载均衡相关的拦截器,这不明摆着这就是负载均衡逻辑的⼊⼝吗,可能性已经⾮常的⼤了。
带着这个想法进去后发现,原来配置这个拦截器的类和⽅法就在下⾯,可以看到在LoadBalancerInterceptor中的restTemplateCustomizer⽅法,就有⼀个匿名内部类,⾥⾯就为 restTemplate设置了拦截器:LoadBalancerInterceptor,到拦截器⾥⾯看下:
看到这⾥,基本上我们能够断定负载均衡相关的核⼼应该就从这⾥开始了。在拦截器LoadBalancerInterceptor的interceptor⽅法中,可以看到⾸先会从originalUri中获取host,⽽这个host不就是Demo中的 http://product-service/product/queryInfo/1指定的请求⽬标服务的服务名product-service吗,然后拿着这个 product-service、通过loadBalancer执⾏execute⽅法。
⽽这⾥的loadBalancer类型就是LoadBalancerClient,⼤家还记得最前⾯我们进⼊@LoadBalanced中看 到的注释吗:
就和这⾥的LoadBalancerClient相呼应了。
终于、我们算是找到了@LoadBalanced注-负载均衡逻辑的⼊⼝位置了。
这⾥想要吐槽⼀下的就是,相⽐于早期Spring都是通过XML⽂件的⽅式配置⼀个⼀个的Bean、然后Spring会解析XML然后注⼊到⾃⼰的容器中,这样找起来也会⽐较⽅便⼀点,直接找XML就⾏了,但是现在这些框架都是通过@Bean等注解的⽅式⾃动配置注⼊,导致寻找⼀个接⼝的真实类的在哪构造时还真的挺费精⼒的。
此时Ribbon的执⾏流程图如下所示: