spring Cloud nacos Ribbon整合源码分析

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。

@Bean
public LoadBalancerInterceptor ribbonInterceptor(
		LoadBalancerClient loadBalancerClient,
		LoadBalancerRequestFactory requestFactory) {
	return new LoadBalancerInterceptor(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);
		}
	}
}
//子容器名称就是serviceId
protected String generateDisplayName(String name) {
	return this.getClass().getSimpleName() + "-" + name;
}
例子

在入口类中加入@RibbonClient

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

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

@Configuration
public class MyConfig {
    @Bean
    public IRule rule() {
        return new RoundRobinRule();
    }
}

如何获取serverList

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

核心接口

IRule
RetryRule

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

public Server choose(ILoadBalancer lb, Object key) {
	answer = subRule.choose(key);
	if (((answer == null) || (!answer.isAlive()))
			&& (System.currentTimeMillis() < deadline)) {
			//开启一个延时任务,中断当前线程
		InterruptTask task = new InterruptTask(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 randomIndex
    int n = 0;
    for (Double d : currentWeights) {
        if (d >= randomWeight) {
            serverIndex = n;
            break;
        } else {
            n++;
        }
    }

    server = allList.get(serverIndex);
}
AvailabilityFilteringRule

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

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值