第七章 对Ribbon的理解

目录

一.概括

二.初始化RestTemplate

三.负载均衡客户端的调用

四.创建负载均衡器

五.根据负载均衡算法选择服务

六.服务调用

七.负载均衡算法

八.总结


一.概括

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请求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值