ribbon的作用是微服务客户端的一个负载均衡发现器,今天我们就讲一下ribbon底层源码是如何从注册中心获取服务列表的,下面我们就讲一下源码是如何实现的

我之前有篇文章讲过,controller通过调用restTemplate.getForObject()方法最终会调用到LoadBalancerInterceptor的intercept方法,

继续进入这个execute方法

在这里会先去获取一个负载均衡器IloadBalancer,我们看看这个this.getLoadBalancer()方法里面做了什么



这里通过getInstance,最后会调用SpringClientFactory父类的getInstance方法,我进入父类NamedContextFactory看看这个getInstance方法做了什么


这里核心的逻辑就是,先去本地缓存获取我们调用服务的上下问对象,如果没有就为每个为我们要调用的微服务创建一个上下文对象,并且放入本地缓存里面,我们重点看看这个createContext方法

在这段代码里面,new了一个AnnotationConfigApplicationContext类,这个类就是Spring的上下文对象,然后调用context的register方法,为容器注册了一个RibbonClientConfiguration配置类,最后调用context的refresh方法,会走spring 容器的生命周期流程,这里就不做过多赘述,我们来看看这个RibbonClientConfiguration这个类
@Configuration(
proxyBeanMethods = false
)
@EnableConfigurationProperties
@Import({HttpClientConfiguration.class, OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
public class RibbonClientConfiguration {
public static final int DEFAULT_CONNECT_TIMEOUT = 1000;
public static final int DEFAULT_READ_TIMEOUT = 1000;
public static final boolean DEFAULT_GZIP_PAYLOAD = true;
@RibbonClientName
private String name = "client";
@Autowired
private PropertiesFactory propertiesFactory;
public RibbonClientConfiguration() {
}
@Bean
@ConditionalOnMissingBean
public IClientConfig ribbonClientConfig() {
DefaultClientConfigImpl config = new DefaultClientConfigImpl();
config.loadProperties(this.name);
config.set(CommonClientConfigKey.ConnectTimeout, 1000);
config.set(CommonClientConfigKey.ReadTimeout, 1000);
config.set(CommonClientConfigKey.GZipPayload, true);
return config;
}
@Bean
@ConditionalOnMissingBean
public IRule ribbonRule(IClientConfig config) {
if (this.propertiesFactory.isSet(IRule.class, this.name)) {
return (IRule)this.propertiesFactory.get(IRule.class, config, this.name);
} else {
ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
rule.initWithNiwsConfig(config);
return rule;
}
}
@Bean
@ConditionalOnMissingBean
public IPing ribbonPing(IClientConfig config) {
return (IPing)(this.propertiesFactory.isSet(IPing.class, this.name) ? (IPing)this.propertiesFactory.get(IPing.class, config, this.name) : new DummyPing());
}
@Bean
@ConditionalOnMissingBean
public ServerList<Server> ribbonServerList(IClientConfig config) {
if (this.propertiesFactory.isSet(ServerList.class, this.name)) {
return (ServerList)this.propertiesFactory.get(ServerList.class, config, this.name);
} else {
ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();
serverList.initWithNiwsConfig(config);
return serverList;
}
}
@Bean
@ConditionalOnMissingBean
public ServerListUpdater ribbonServerListUpdater(IClientConfig config) {
return new PollingServerListUpdater(config);
}
@Bean
@ConditionalOnMissingBean
public ILoadBalancer ribbonLoadBalancer(IClientConfig config, ServerList<Server> serverList, ServerListFilter<Server> serverListFilter, IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
return (ILoadBalancer)(this.propertiesFactory.isSet(ILoadBalancer.class, this.name) ? (ILoadBalancer)this.propertiesFactory.get(ILoadBalancer.class, config, this.name) : new ZoneAwareLoadBalancer(config, rule, ping, serverList, serverListFilter, serverListUpdater));
}
@Bean
@ConditionalOnMissingBean
public ServerListFilter<Server> ribbonServerListFilter(IClientConfig config) {
if (this.propertiesFactory.isSet(ServerListFilter.class, this.name)) {
return (ServerListFilter)this.propertiesFactory.get(ServerListFilter.class, config, this.name);
} else {
ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
filter.initWithNiwsConfig(config);
return filter;
}
}
@Bean
@ConditionalOnMissingBean
public RibbonLoadBalancerContext ribbonLoadBalancerContext(ILoadBalancer loadBalancer, IClientConfig config, RetryHandler retryHandler) {
return new RibbonLoadBalancerContext(loadBalancer, config, retryHandler);
}
@Bean
@ConditionalOnMissingBean
public RetryHandler retryHandler(IClientConfig config) {
return new DefaultLoadBalancerRetryHandler(config);
}
@Bean
@ConditionalOnMissingBean
public ServerIntrospector serverIntrospector() {
return new DefaultServerIntrospector();
}
@PostConstruct
public void preprocess() {
RibbonUtils.setRibbonProperty(this.name, CommonClientConfigKey.DeploymentContextBasedVipAddresses.key(), this.name);
}
static class OverrideRestClient extends RestClient {
private IClientConfig config;
private ServerIntrospector serverIntrospector;
protected OverrideRestClient(IClientConfig config, ServerIntrospector serverIntrospector) {
this.config = config;
this.serverIntrospector = serverIntrospector;
this.initWithNiwsConfig(this.config);
}
public URI reconstructURIWithServer(Server server, URI original) {
URI uri = RibbonUtils.updateToSecureConnectionIfNeeded(original, this.config, this.serverIntrospector, server);
return super.reconstructURIWithServer(server, uri);
}
protected Client apacheHttpClientSpecificInitialization() {
ApacheHttpClient4 apache = (ApacheHttpClient4)super.apacheHttpClientSpecificInitialization();
apache.getClientHandler().getHttpClient().getParams().setParameter("http.protocol.cookie-policy", "ignoreCookies");
return apache;
}
}
}
很明显这个类就是一个配置类,当context.refresh时候,会将这个类里面的加了@Bean注解的类给实例化成Bean对象,这里有个关键类ILoadBalance类

最终会走到new ZoneAeareLoadBalancer这段逻辑,继续跟进去

ZoneAwareLoadBalancer构造方法初始化的时候,会调用父类DynamicServerListLoadBalancer里面的的构造方法

在父类DynamicServerListLoadBalancer里面的构造方法又会调用restOfInit方法


在这个updateListOfServers里面会调用到this.serverListImpl.getUpdatedListOfServers()方法,

因为我这里注册中心的用的nacos,引入的是nacos的相关依赖,所以这里会调用NacosServerList这个类的getUpdatedListOfServers方法

这里会调用到NacosNamingService里面的selectInstances方法


这里默认subscribe是true,最终会调用 this.hostReactor.getServiceInfo()方法,

在这个方法里面,重点关注一下scheduleUpdateIfAbsent方法,看命名应该是一个定时更新的方法,点进去看一下

这里会执行addTask方法,入参是一个UpdateTask类,我们看看这个UpdateTask是一个什么类

很明显UpdateTask实现了Runnable是一个线程类,回到addTask方法里面,这里会调用一个定时任务去执行UpdateTask的run方法

在UpdateTask中run方法会调用HostReactor.this.updateSrvice方法,

在这里会接着调用serverProxy.queryList方法

这里首先封装http接口需要的请求参数,最终通过http请求调用nacos提供的api接口,到这里整个Ribbon底层如何获取注册中心的服务列表就已经完成了
本文深入探讨Ribbon作为微服务客户端的负载均衡器如何通过源码从注册中心(如Nacos)获取服务列表。通过跟踪`LoadBalancerInterceptor`、`ILoadBalancer`、`ZoneAwareLoadBalancer`等关键类,详细解析了从创建Spring上下文到执行HTTP请求获取服务信息的完整过程。

560

被折叠的 条评论
为什么被折叠?



