使用场景
@GetMapping("testChoose")
public ServiceInstance testChoose() {
// 与Feign或RestTemplate均衡机制相同,都是基于ILoadBalancer&Rule来进行的
return loadBalancerClient.choose("provider-app");
}
本次基于RibbonLoadBalancerClient(在自动装配过程中,它的优先级比较高)来分析
初始化LoadBalancerClient
// org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration
// 用来创建Client Context, 每个ClientContext都是一个独立的上下文
@Bean
public SpringClientFactory springClientFactory() {
SpringClientFactory factory = new SpringClientFactory();
factory.setConfigurations(this.configurations);
return factory;
}
// 客户端负载均衡的入口
@Bean
@ConditionalOnMissingBean(LoadBalancerClient.class)
public LoadBalancerClient loadBalancerClient() {
return new RibbonLoadBalancerClient(springClientFactory());
}
LoadBalancerClient的底层选择逻辑基于如下两个操作完成
//loadbalancer init, 这是关键逻辑,超时,规则,重试都是基于此对象完成的
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
// 根据LoadBalancer选择匹配的Server
Server server = getServer(loadBalancer, hint);
ILoadBalancer的初始化
// org.springframework.cloud.netflix.ribbon.SpringClientFactory
/**
* Get the load balancer associated with the name.
* @param name name to search by
* @return {@link ILoadBalancer} instance
* @throws RuntimeException if any error occurs
*/
public ILoadBalancer getLoadBalancer(String name) {
return getInstance(name, ILoadBalancer.class);
}
public SpringClientFactory() {
// 生成ILoadBalancer过程中会基于RibbonClientConfiguration来配置
super(RibbonClientConfiguration.class, NAMESPACE, "ribbon.client.name");
}
protected AnnotationConfigApplicationContext createContext(String name) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 基于注解的Ribbon配置
if (this.configurations.containsKey(name)) {
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);
}
}
}
context.register(PropertyPlaceholderAutoConfiguration.class,
// 默认配置模板
this.defaultConfigType);
context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
this.propertySourceName,
// 添加客户端名称
//@Value("${ribbon.client.name}")public @interface RibbonClientName
// this.propertyName = ribbon.client.name
Collections.<String, Object>singletonMap(this.propertyName, name)));
if (this.parent != null) {
// Uses Environment from parent as well as beans
context.setParent(this.parent);
// jdk11 issue
// https://github.com/spring-cloud/spring-cloud-netflix/issues/3101
context.setClassLoader(this.parent.getClassLoader());
}
context.setDisplayName(generateDisplayName(name));
context.refresh();
return context;
}
基于配置文件的初始化
// 配置读取源
@Bean
@ConditionalOnMissingBean
public PropertiesFactory propertiesFactory() {
return new PropertiesFactory();
}
// org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration#ribbonLoadBalancer
@Bean
@ConditionalOnMissingBean
public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
// 使用基于配置的初始化
if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {
return this.propertiesFactory.get(ILoadBalancer.class, config, name);
}
return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
serverListFilter, serverListUpdater);
}