Ribbon源码是如何获取注册中心的服务列表

本文深入探讨Ribbon作为微服务客户端的负载均衡器如何通过源码从注册中心(如Nacos)获取服务列表。通过跟踪`LoadBalancerInterceptor`、`ILoadBalancer`、`ZoneAwareLoadBalancer`等关键类,详细解析了从创建Spring上下文到执行HTTP请求获取服务信息的完整过程。

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

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底层如何获取注册中心的服务列表就已经完成了

 

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值