目录
一.概括
Ribbon这个组建就是利用负载均衡的策略和RestTemplate调用远程服务,而且不用知道远程服务的ip和端口,只需要知道远程服务名就可以了。
Ribbonde 有两个作用
1.调用负载均衡器根据相应的规则选择某一个服务进行调研
2.将逻辑url,也就是服务名换成ip加端口号的形式
Ribbon的大体原理就是在RestTemplate发出请求之前利用拦截器进行拦截,在拦截器中创建负载均衡器(第一次调用的时候),负载均衡器从EurekaClient端获取到已经拉取到本地的服务列表,然后根据配置的负载均衡策略选取一个服务进行调用,调用的时候会将服务名替换成ip和端口号的形式进行调用。
二.初始化RestTemplate
/**
* Auto configuration for Ribbon (client side load balancing).
*
* @author Spencer Gibb
* @author Dave Syer
*/
//表示这是一个配置类
@Configuration
//必须存在RestTemplate类
@ConditionalOnClass(RestTemplate.class)
//必须存在LoadBalancerClient类型的bean
@ConditionalOnBean(LoadBalancerClient.class)
public class LoadBalancerAutoConfiguration {
//所有被@LoadBalanced注解修饰的RestTemplate会被放入到restTemplates中
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
//对于所有被@LoadBalanced注解修饰的RestTemplate,调用SmartInitializingSingleton的
//customize方法,将拦截器放入到RestTemplate中
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializer(
final List<RestTemplateCustomizer> customizers) {
return new SmartInitializingSingleton() {
@Override
public void afterSingletonsInstantiated() {
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
for (RestTemplateCustomizer customizer : customizers) {
customizer.customize(restTemplate);
}
}
}
};
}
//对于所有被@LoadBalanced注解修饰的RestTemplate,增加loadBalancerInterceptor属性
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return new RestTemplateCustomizer() {
@Override
public void customize(RestTemplate restTemplate) {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
}
};
}
//产生一个LoadBalancerInterceptor类型的bean,包含loadBalancerClient类型的bean
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient) {
return new LoadBalancerInterceptor(loadBalancerClient);
}
}
在启动服务的时候,通过LoadBalancerAutoConfiguration这个配置类,会创建带有LoadBalanceClient的拦截器,然后将这个拦截器注入到带有@LoadBalance注解的RestTemplate。这样我们在调用RestTemplate发送请求的时候首先会被拦截器拦截。
二.RestTemplate的getObject的调用
RestTemplate类
HttpAccessor类
为什么从RestTempate类一下到了HttpAccessor类中呢?查看RestTempate的继承关系,RestTempate继承了InterceptingHttpAccessor,InterceptingHttpAccessor继承了HttpAccessor,InterceptingHttpAccessor重写了getRequestFactory方法。
先看getRequestFactory方法
可以看到创建的request工厂是InterceptingClientHttpRequestFactory,查看InterceptingClientHttpRequestFactory的createRequest方法
总结上面的代码,其实就做了一件事,创建InterceptingClientHttpRequest,并将RestTemplate中的拦截器给InterceptingClientHttpRequest。为的就是在request发起请求的时候能被拦截器拦截。
再回到RestTempalte的doExecute方法
查看InterceptingClientHttpRequest类的继承关系
调用InterceptingClientHttpRequest的execute方法,其实是调用AbstractClientHttpRequest的execute方法。
调用executeInternal方法,在AbstractClientHttpRequest中AbstractClientHttpRequest方法是抽象方法,AbstractClientHttpRequest的子类AbstractBufferingClientHttpRequest对executeInternal方法进行了重写
InterceptingClientHttpRequest继承AbstractBufferingClientHttpRequest并实现了executeInternal方法。
从RestTemplate中的 response = request.execute(); 代码其实就是最后调用InterceptingClientHttpRequest的executeInternal方法。只是在这个过程中给request设置了请求头。
上面搞得比较复杂,其实用一句话概括就是要看到拦截器被调用。
下面的才是重点,重点就发生在负载均衡客户端的execute方法中
三.负载均衡客户端的调用
看一下RibbonLoadBalancerClient负载均衡客户端的核心方法。
截图中的RibbonServer的作用说错了,应该是RibbonStatsRecorder的作用
这个方法比较重要,总结一下这个方法的流程
1.根据服务名称从ribbon子容器中从获取负载均衡器,如果是第一次调用就会创建这个服务对象的子容器,起到配置隔离的作用,这也是为什么ribbon第一次调用服务的时候很容易出现超时的问题,这是因为需要创建子容器。
2.负载均衡器根据负载均衡算法,选中一个服务进行调用。
3.构建RibbonServer,记录服务花费时间,服务并发数,为负载均衡算法选取服务提供数据参考
4.执行请求。
四.创建负载均衡器
获取负载均衡器
SpringClientFactory类
子容器的作用是起到一个配置隔离的效果,例如客户端A要调用远程服务B和C,那么可以再配置负载均衡算法以及Ping算法的时候,B和C的配置可以不一样,B配随机,C配轮询。至于如何配置才能起到配置隔离我在网上没有搜索到。
负载均衡器的实现类:DynamicServerListLoadBalancer的作用:
1.去eurekaclient读取配置(读取服务列表)
2.定时配置刷新,每30秒一次去读取服务列表。
3.ping 要调用的eurekaclient的服务 默认是不会去ping的,因为去操作这个ping的是一个定时任务,每10秒一次,要是服务很多,就很容易在10秒内ping不完,默认的实现是通过拿到服务列表后,根据服务的状态是不是UP来判断的ping的结果的。
看到服务列表是从DynamicServerListLoadBalancer中获取到服务列表的
先看updateListOfServers方法
DiscoveryEnabledNIWSServerList类的getUpdatedListOfServers方法
DiscoveryClient类的getInstancesByVipAddress方法
可以确定服务列表就是从EurekaClient本地拿取到的
定时更新服务列表
进入到PollingServerListUpdater类中
五.根据负载均衡算法选择服务
直接看是如何选取服务的,这个就是ribbon的核心
BaseLoadBalancer类的chooseServer方法
可以看出rule的实现类是ZoneAvoidanceRule,其实ZoneAvoidanceRule最终还是一种轮训算法来获取服务,查看ZoneAvoidanceRule的继承关系,ZoneAvoidanceRule的choose方法其实是PredicateBasedRule的choose方法。
PredicateBasedRule的choose方法
AbstractServerPredicate类的chooseRoundRobinAfterFiltering方法
AbstractServerPredicate类的incrementAndGetModulo方法
至此获取到要请求的服务了。总结一下请求服务的获取过程
1.当调用request执行的时候,就会调用拦截器执行
2.拦截器里面会根据url获取要调用服务的服务名称并传给负载均衡客户端loadbalanceClient执行请求
3.loadbalanceClient里面做了三件事,第一件就是当第一次调用的时候会创建这个服务对应的子容器,在子容器里面获取到负载均衡器。第二件事就是利用负载均衡器来从服务列表中获取一个服务来调用。负载均衡器会根据rule这个接口的实现类来从选取服务,服务列表是从DynamicServerListLoadBalancer这里获取到
4.调用服务。
六.服务调用
选取完要调用的服务对象,下面看一下服务调用的过程
走到了LoadBalancerRequestFactory类中的createRequest方法。
进入到InterceptingClientHttpRequest的execute方法中
最后走到SimpleBufferingClientHttpRequest的executeInternal方法。
七.负载均衡算法
负载均衡有好几种实现策略,常见的有:
随机 (Random)
轮询 (RoundRobin)
一致性哈希 (ConsistentHash)
哈希 (Hash)
加权(Weighted)
负载均衡参考文章:
https://blog.csdn.net/wudiyong22/article/details/80829808
八.总结
总结一下ribbon的调用过程
1.给@LoadBalance修饰的RestTemlate设置拦截器。
2.当RestTemlate发送请求的时候,先创建request对象,会将拦截器设置到request对象中,
3.当执行restTemplate的execute方法的时候,会调用request的execute方法,调用拦截器里面的拦截方法。
4.在拦截器中会从逻辑url中取出服务名,服务名为后面创建子容器备用,然后调用负载均衡客户端获取服务。
5.到了负载均衡客户端中,如果是ribbon首次执行这个服务的请求,会先去创建一个子容器,从子容器中取出负载均衡器,负载均衡器会去获取Eureka Client里面的服务列表,以及创建一个定时任务定时刷新服务列表,然后负载均衡器会从服务列表中利用Rule对象选出一个服务,这个Rule算法默认是轮训算法。
6.拿到服务对象后,将url改成ip加端口的形式进行服务调用,返回结果。
Ribbon的作用就是根据服务名利用负载均衡算法获取到要调用的服务信息,然后根据服务信息将服务名换成ip加端口号发送http请求。