在上一篇中写到了,关于注册中心、提供者和消费者三者之间,在简单的集群中是如何调用的,那这一篇就具体介绍下,消费者中ribbon是如何通过restTemplate来实现负载均衡的,要想知道这个答案,那我们得从源码上来分析
首先,我们可以知道我们之前是通过下面的内容来实现负载均衡的
通过初步观察,我们可以了解到,负载均衡的实现是通过@loadBalanced的注解来完成的(loadBalanced从字面翻译也是“负载平衡”的意思),所以要想知道具体的缘由,那么就跟进源码去看看
这里我们选中@loadBalanced,按住Ctrl后,再配合鼠标的点击就进入到源码中,可以看到下面的内容:
这里翻译下注释:
"Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient"
意思是:标记要配置为使用 LoadBalancerClient的RestTemplate中bean的注释(我通过百度翻译的)
之后我们搜索下LoadBalancerClient,可以发现这是一个接口,接口内容如下:
解释:
ServiceInstance choose(String serviceId):根据传入的服务名serviceId,从负载均衡器中挑选一个对应服务的实例
T execute(String serviceId, LoadBalancerRequest request) throws IOException:使用从负载均衡器中挑选出的服务实例来执行请求内容
URI reconstructURI(ServiceInstance instance, URI original):为系统构建一个合适形式的URI
然后我们再通过LoadBalancerClient接口所属于的包:
进入包中,可以查看出:
其中,LoadBalancerAutoConfiguration为实现客户端负载均衡的自动化配置类,可以通过源码来查看:
说明一下LoadBalancerAutoConfiguration类上的两个注解的意思:
@ConditionalOnClass(RestTemplate.class):RestTemplate类必须存在与当前工程的环境中
@ConditionalOnBean(LoadBalancerClient.class):在Spring的Bean工程中必须有LoadBalancerClient的实现bean
接下来,再说明下,这个类主要做了哪些事情:
创建了一个LoadBalancerInterceptor的Bean,用于实现对客户端发起请求时进行拦截,以实现客户端负载均衡。
创建了一个RestTemplateCustomizer的Bean,用于给RestTemplate增加LoadBalancerInterceptor拦截器
维护了一个被@LoadBalaned注释修饰的RestTemplate对象列表,并在这里进行初始化
接着我们再跟随LoadBalancerInterceptor拦截器,看它是如何将一个普通的RestTemplate变成客户端负载均衡的:
通过源码,我们可以了解到,拦截器中注入了LoadBalancerClient的实现
过程解析:当一个被@LoadBalanced注解的RestTemplate对象向外发起HTTP请求时,会被LoadBalancerInterceptor类的intercept方法所拦截。由于我们使用RestTemplate时采用了服务名为host,所以直接从HttpRequest的URI对象中通过getHost()就可以拿到服务名,然后调用execute方法去根据服务名来选择实例并发起实际请求。
写到这里,了解到LoadBalancerClient却只是个抽象的负载均衡接口,我们要去根据它的实现类再做进一步的查看
通过图上,红圈中的execute方法跟进,能得到如下内容:
我们从上面的红圈中可以看出,getServer方法时根据传入的serviceId来获取具体的服务实例
那我们再跟进一下getServer方法中:
这时,我们通过getServer的源码中可以看出,获取具体的实例的时候,并没有通过LoadBalancerClient中的ServiceInstance choose(String serviceId)的方法来获取,而是采用了ILoadBalancer接口中定义的chooseServer的方法来获取的,所以接下来,我们继续去查看下ILoadBalancer这个接口,如下:
这里分别介绍下这个接口中的5个抽象的方法(其实是6个,但是其中一个已经过时不用了)
addServers:向负载均衡中维护的实例列表增加服务实例
chooseServer:通过某种策略,从负载均衡中挑选出一个具体的服务实例
markServerDown:用来通知和表示负载均衡器中某个具体实例已经停止服务,不然负载均衡器再下一次获取服务实例清单前都会认为服务实例均是正常服务的
getReachableServers:获取当前正常服务的实例列表
getAllServers:获取所有已知的服务实例列表,包括正常服务和停止服务的实例
通过查看接口的实现,我们可以了解到,BaseLoadBalancer类实现了基础的负载均衡,而DynamicServerListLoadBalancer和ZoneAwareLoadBalancer在负载均衡的策略上做了一些功能的扩展
在整合Ribbon的时候,我们可以了解到Spring Cloud是怎么具体实现的,通过
RibbonClientConfiguration配置类,我们了解到整合时默认采用了ZoneAwareLoadBalancer来实现负载均衡的
这里我们再回到LoadBalancerClient的具体实现类RibbonLoadBalancerClient上的execute方法上,通过ZoneAwareLoadBalancer的chooseServer方法获取了负载均衡策略分配到的具体实例对象server后,将其内容包装成RibbonServer对象
然后再使用该对象回调LoadBalancerInterceptor请求拦截器中LoadBalancerRequest的apply方法,向一个实际的具体服务实例发起请求,实现服务名为host的URI请求到host:port形式的实际访问地址的转换
在apply方法中传入的ServiceInstance接口对象是对服务实例的抽象定义:
在这个接口中暴露了服务治理系统中每个服务实例需要提供的一些基本信息,比如servceId、host、port等,如图:
而之前包装服务实例RibbonServer对象就是ServiceInstance接口的实现,可以发现除了包含Server对象之外,还储存了服务名、是否使用HTTPS标识以及一个Map类型的元数据集合,如图:
apply方法在接受到了具体的ServiceInstance实例后,通过LoadBalancerClient接口中reconstructURI操作来组织具体的请求地址,如下:
接下来分析apply.... ....
待续
领取专属 10元无门槛券
私享最新 技术干货