记SpringCloud Finchley.SR4中使用ribbon.listOfServers无效的坑

在将SpringCloud从Dalston.RELEASE升级到Finchley.SR4后,发现ribbon.listOfServers配置失效,服务调用失败。分析源码发现,由于EurekaRibbonClientConfiguration的存在,导致配置的服务器列表未被使用。解决方案包括移除Eureka依赖、指定NIWSServerListClassName或禁用ribbon.eureka.enabled。

前一篇文章提到了我将项目的SpringCloud版本由Dalston.RELEASE升级到了Finchley.SR4。因为我没有使用Eureka,而是使用的ribbon.listOfServers来指定服务器,然后进行服务调用的,所以这里在升级后就遇到了一个坑:就是先前Dalston.RELEASE版本的时候服务可以调用成功,现在升级后反而服务调不通了。
懊恼痛苦中。。。。。。。。。。。。
我通过查看消费端的日志,发现消费端根本就没有获取到服务器地址。日志如下:

2021-05-18 20:17:06.003  INFO 44936 --- [           main] c.netflix.loadbalancer.BaseLoadBalancer  : Client: SPRINGCLOUD-PROVIDER instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=SPRINGCLOUD-PROVIDER,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
2021-05-18 20:17:06.012  INFO 44936 --- [           main] c.n.l.DynamicServerListLoadBalancer      : Using serverListUpdater PollingServerListUpdater
2021-05-18 20:17:06.021  INFO 44936 --- [           main] c.n.l.DynamicServerListLoadBalancer      : DynamicServerListLoadBalancer for client SPRINGCLOUD-PROVIDER initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=SPRINGCLOUD-PROVIDER,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@496cedbb

发现问题

于是我就纳闷,是什么原因导致ribbon.listOfServers失效了呢?

带着这个问题,我在网上查了资料,同时也通过自己的分析,发现日志中的服务信息是在DynamicServerListLoadBalancer这个类中打印的,那么服务信息是怎么传递到这个类中的呢?
查看源码,原来是通过ribbonLoadBalancer()方法中的new ZoneAwareLoadBalancer()来传递的服务信息的:在这里插入图片描述
因此,服务信息就是ribbonLoadBalancer()方法的serverList参数决定的。
由于我们引入了spring-cloud-starter-netflix-eureka-client依赖,所以在配置类【RibbonClientConfiguration】和配置类【EurekaRibbonClientConfiguration】中均定义了获取该参数实例的@Bean方法【ribbonServerList】,并且都被【@ConditionalOnMissingBean】注解标注。这两者在上下文环境即配置文件中未指定参数【NIWSServerListClassName】的值时将会返回不同的默认类型。
【RibbonClientConfiguration】返回的是从参数【listOfServers】中获取服务器列表的【ConfigurationBasedServerList】,而【EurekaRibbonClientConfiguration】返回的是从Eureka服务端获取服务器列表的【DomainExtractingServerList】。

那么这里引入了一个问题。程序会加载配置类【RibbonClientConfiguration】和配置类【EurekaRibbonClientConfiguration】两个中的哪一个@Bean方法【ribbonServerList】呢?

分析过程
这里我在分析源码后,梳理了一下流程,分享给大家:
1.程序在启动时,会执行RibbonClientConfigurationRegistrar类,这个类里面会去扫描@RibbonClients和@RibbonClient注解,然后获取到它们的注解配置信息,将注解属性name和configuration的值设置到一个RibbonClientSpecification类型的对象中,再将该对象注册为spring容器中。代码截图如下:
在这里插入图片描述
这里我根据断点调试后发现有3个和@RobbinClient或@RobbinClients注解有关的配置。
1).我自己项目中自定义的配置:
@RibbonClient(name = “SPRINGCLOUD-PROVIDER”,configuration = MyConfiguration.class)
2).RibbonAutoConfiguration类,该类上有@RibbonClients注解
3).RibbonEurekaAutoConfiguration类,该类上有注解:@RibbonClients(defaultConfiguration = EurekaRibbonClientConfiguration.class)

2.在将多个RibbonClientSpecification类型的bean注册到spring容器后,在RibbonAutoConfiguration类中进行List注入,然后在以@Bean注解的springClientFactory()方法里面进行获取,然后设置给【SpringClientFactory】对象。
在这里插入图片描述
这里的【SpringClientFactory】是个每个客户端用来创建Spring上下文的。会给每个Ribbon Client创建一个独立的Spring应用上下文ApplicationContext,并在其中加载对应的配置及Ribbon核心接口的实现类。

3.接着会执行RibbonApplicationContextInitializer的onApplicationEvent()方法,这个方法会调用springClientFactory.getContext(clientName)方法,继而会调NamedContextFactory类的getContext(String name)方法,然后又会调createContext(String name)方法,
在这里插入图片描述

在这里插入图片描述

这里的configurations包含3个元素,如下:
在这里插入图片描述

虽然前面在【RibbonAutoConfiguration】类中用@Bean注解定义SpringClientFactory且在创建SpringClientFactory对象时会给NamedContextFactory类的defaultConfigType属性赋值为RibbonClientConfiguration.class,但是在createContext()方法中属性【configurations】是优先于【defaultConfigType】,因为是先通过configurations里的对象进行context.register()注册到容器的。又因为configurations中的【default.org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration】没有指定配置类,也就是它的configuration属性为空,所以这个配置被忽略了,于是就注册了【EurekaRibbonClientConfiguration】对象。再因为@ConditionalOnMissingBean注解的原因,所以就不会执行【RibbonClientConfiguration】类的ribbonServerList()方法,只会执行【EurekaRibbonClientConfiguration】的ribbonServerList()方法。但是在此之前,它会先执行RibbonClientConfiguration的ribbonClientConfig()方法去获取配置信息。
在这里插入图片描述
在EurekaRibbonClientConfiguration的ribbonServerList()方法中,首先会先查看PropertiesFactory 里是否已经设置了此组件对应的数据。如果是,那么返回预先定义好的;如果不是,那么实时生成。
这样在EurekaRibbonClientConfiguration的ribbonServerList()方法中就可以直接获取到IClientConfig对象了:
在这里插入图片描述

在这里插入图片描述
因此,这里创建的ServerList对象就不包含了我们配置的ribbon.listOfServers的服务配置信息了。

解决方案
那么要怎么解决这个问题呢?
1、最简单的就是移除依赖【spring-cloud-starter-netflix-eureka-client】。
2、在不移除该依赖的情况下,可以通过在配置文件中指定参数【NIWSServerListClassName】为【com.netflix.loadbalancer.ConfigurationBasedServerList】
3、若不想移除依赖,那么可以设置属性【ribbon.eureka.enabled】为false,即可阻止其对Spring上下文生效。

补充
那么ribbon.listOfServers是在哪里加载的呢?
是在loadProperties()里面加载的,如下截图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值