spring Cloud nacos Ribbon整合源码分析

2022-06-14 11:45:36

LoadBalanced restTemplate

首先在spring.cloud.common包的spring.factories中定义了org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration。LoadBalancerAutoConfiguration中获取了@LoadBalanced的RestTemplate。

@LoadBalanced@Autowired(required=false)private List<RestTemplate> restTemplates= Collections.emptyList();

然后对被@LoadBalanced标注的restTemplates注入拦截器LoadBalancerInterceptor。

@Beanpublic LoadBalancerInterceptorribbonInterceptor(
		LoadBalancerClient loadBalancerClient,
		LoadBalancerRequestFactory requestFactory){returnnewLoadBalancerInterceptor(loadBalancerClient, requestFactory);}

restTemplate的http请求的方法,最终都会调用到doExecute()方法,在doExecute()方法中会创建InterceptingClientHttpRequest,这个request包含了刚才被注入的拦截器,在真正的http调用之前,会先执行负载均衡拦截器的intercept方法,在该方法中又会调用RibbonLoadBalancerClient的execute方法。

主要逻辑

execute方法中,先获取ILoadBalancer实例,在调用ILoadBalancer实例的chooseServer方法获取到具体Server实例

ILoadBalancer loadBalancer=getLoadBalancer(serviceId);
Server server=getServer(loadBalancer, hint);

ILoadBalancer如何获取

会根据serviceId创建一个子容器,让这个子容器去加载@RibbonClient或@RibbonClients引入的配置类(默认是RibbonClientConfiguration,如果自定义了配置类会覆盖默认配置,conditinalOnMissingBean),引入的配置类中含有ILoadBalancer,再从子容器获取ILoadBalancer bean(默认是ZoneAwareLoadBalancer)。

RibbonLoadBalancerClient.getLoadBalancer(String serviceId)
    SpringClientFactory.getLoadBalancer(String name)getInstance(name, ILoadBalancer.class)super.getInstance(name, type)getContext(String name)createContext(String name)
                context.getBean(type)

NamedContextFactory#createContext(String name):

//自定义配置类if(this.configurations.containsKey(name)){//configurations就是RibbonClient通过xxRegistrar引入引入的配置类for(Class<?> configuration:this.configurations.get(name).getConfiguration()){
		context.register(configuration);}}//默认配置类for(Map.Entry<String, C> entry:this.configurations.entrySet()){if(entry.getKey().startsWith("default.")){for(Class<?> configuration: entry.getValue().getConfiguration()){
			context.register(configuration);}}}//子容器名称就是serviceIdprotected StringgenerateDisplayName(String name){returnthis.getClass().getSimpleName()+"-"+ name;}

例子

在入口类中加入@RibbonClient

@EnableDiscoveryClient@SpringBootApplication@RibbonClient(name="nacos-payment-provider",configuration= MyConfig.class)publicclassOrderNacosMain83

自定义配置类,其实被spingboot入口类扫到也无大碍,只不过父容器也会定义一份ribbon相关的bean,而这些用不到,只会用子容器的

@ConfigurationpublicclassMyConfig{@Beanpublic IRulerule(){returnnewRoundRobinRule();}}

如何获取serverList

ZoneAwareLoadBalancer构造方法执行时,会调用NacosNamingService#selectInstances(String serviceName, String groupName, List clusters, boolean healthy, boolean subscribe).

核心接口

IRule

RetryRule

底层还是调用子RUle(默认RoudRobinRule),如果返回的Server不可用,如果会在一个时间范围内重试(默认500ms)。

public Serverchoose(ILoadBalancer lb, Object key){
	answer= subRule.choose(key);if(((answer== null)||(!answer.isAlive()))&&(System.currentTimeMillis()< deadline)){//开启一个延时任务,中断当前线程
		InterruptTask task=newInterruptTask(deadline- System.currentTimeMillis());while(!Thread.interrupted()){
			answer= subRule.choose(key);if(((answer== null)||(!answer.isAlive()))&&(System.currentTimeMillis()< deadline)){/* pause and retry hoping it's transient */
				Thread.yield();}else{break;}}
		task.cancel();}if((answer== null)||(!answer.isAlive())){return null;}else{return answer;}}
BestAvailableRule

字面意思上理解是并发连接数最少的Server

WeightedResponseTimeRule

根据平均响应时间来选择,权值=总的响应时间-单个响应时间+前一个权值,平均响应时间小的权重大,会维护一个权重集合,定时更新这个集合,这个集合的每个元素的权值是前面元素的权值和,权值大的说明区间范围大,被选中的概率更大,并不是一定会选择权值最大的。权值还没更新时,使用RoundRobin作为备选

比如A,B,C,D4个节点,平均响应时间分别为10,5,12,20,总响应时间47,

计算出来的权值为:37,79,114,141

相当于得到4个区间:[0,37] [37,79] [79,114] [114,141]

再从0~141随机取值,第一个大于随机值的权值即为选中Server

if(maxTotalWeight<0.001d|| serverCount!= currentWeights.size()){
    server=super.choose(getLoadBalancer(), key);if(server== null){return server;}}else{// generate a random weight between 0 (inclusive) to maxTotalWeight (exclusive)//0~总权值内随机获取double randomWeight= random.nextDouble()* maxTotalWeight;// pick the server index based on the randomIndexint n=0;for(Double d: currentWeights){if(d>= randomWeight){
            serverIndex= n;break;}else{
            n++;}}

    server= allList.get(serverIndex);}
AvailabilityFilteringRule

也是对RoundRobin的封装,会对Server可用性进行过滤,熔断了的或活动的请求数超出限制的,在剩下的Server中轮询

  • 作者:cjay_fighting
  • 原文链接:https://blog.csdn.net/baidu_32223873/article/details/112913342
    更新时间:2022-06-14 11:45:36