dubbo 配置 loadbalance 不生效?撸一把源码
背景
很久之前我给业务方写了一个 dubbo loadbalance 的扩展(为了叙述方便,这个 loadbalance 扩展就叫它 XLB 吧),这两天业务方反馈说 XLB 不生效了
我心想,不可能啊,都用了大半年了~
排查
于是我登上不生效的 consumer 机器进行排查,还好我留了一手,当 XLB 加载时,会打印一行日志
看了下这个服务,并没有打印日志,说明 XLB 并没有加载成功
于是,我就去问对应的开发,有按照我的文档配置 loadbalance 吗?答复:完全按照文档配置
这下我就有点不相信了,但转念一想,配置 loadbalance 如此简单,不应该出错啊,我的文档和他的应用都在 xml 文件中配置了 consumer 的 loadbalance
<dubbo:consumer loadbalance="xlb"/>
抱着试一试的态度,拉取了他们项目的代码,发现配置确实如上,但我发现他们的 application.properties 配置文件也配了一个 consumer 的属性
dubbo.consumer.check=false
以多年和 dubbo 打交道的经验来说,这里有问题,又确认了代码,确实 xml 和 application.properties 都加载了
那这里可能就有问题了,dubbo 从 xml 加载生成了一个 consumer 配置,dubbo-springboot-starter 又从 application.properties 加载配置生成了一个 consumer 配置,这不就冲突了?
别看只配置了 dubbo.consumer.check,它实际上会生成一个完整的 consumer 配置,只不过 loadbalance 为默认值
业务方为什么会这样配置?大概率是因为我的文档里只给出了 xml 形式的配置,没有给 spring-boot 配置,他们原先使用的是 spring-boot 的配置方式,然后看到我的文档是 xml,结果就不会配置了,也写了个 xml,和原先的配置冲突
验证
为了验证是这个问题导致,我把他的 application.properties 的 dubbo.consumer.check 配置挪到了 xml 文件中,果然重启后就加载到了 XLB
随后我又在本地的测试应用上做了这样一个验证:
<!-- case 1 -->
<dubbo:consumer />
<dubbo:consumer loadbalance="xlb"/>
<!-- case 2 -->
<dubbo:consumer loadbalance="xlb"/>
<dubbo:consumer />
两组配置相同,但顺序不同,测试结果为 case 1 可以加载到 XLB,case 2 不行
于是猜测,dubbo consumer 配置以后加载的为准
撸源码
显然猜测不符合我的风格,下面开撸源码,不感兴趣可以划过,最下面有总结
首先搞清楚,何时会加载 loadbalance,在 AbstractClusterInvoker 的 invoke 方法中,加载了 loadbalance
@Override
public Result invoke(final Invocation invocation) throws RpcException {
...
List<Invoker<T>> invokers = list(invocation);
LoadBalance loadbalance = initLoadBalance(invokers, invocation);
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
return doInvoke(invocation, invokers, loadbalance);
}
加载代码如下
protected LoadBalance initLoadBalance(List<Invoker<T>> invokers, Invocation invocation) {
if (CollectionUtils.isNotEmpty(invokers)) {
return ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
.getMethodParameter(RpcUtils.getMethodName(invocation), LOADBALANCE_KEY, DEFAULT_LOADBALANCE));
} else {
return ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(DEFAULT_LOADBALANCE);
}
}
带缓存的加载扩展
public T getExtension(String name) {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
}
if ("true".equals(name)) {
return getDefaultExtension();
}
final Holder<Object> holder = getOrCreateHolder(name);
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
可以看出
- loadbalance 是发起 dubbo 调用时,且当
invokers非空时(即 providers 非空)会被初始化,后续都从缓存中取 - loadbalance 是根据第一个 invoker 的 loadbalance 参数决定使用哪个 loadbalance 的
于是问题转移到 invoker 的 loadbalance 从哪来?provider 不会配置 loadbalance,所以这个参数一定是从 consumer 的配置上得到的
顺藤摸瓜,在 RegistryDirectory 的 toInvokers 方法中调用了 mergeUrl,它是在注册中心通知时被调用,也就是从注册中心上拿到 provider url 时,还得 merge 一下才能用,merge 了些什么内容?
private URL mergeUrl(URL providerUrl) {
// 1. merge consumer 参数
providerUrl = ClusterUtils.mergeUrl(providerUrl, queryMap);
// 2. merge configurator 参数
providerUrl = overrideWithConfigurator(providerUrl);
...
return providerUrl;
}
1中 merge 了queryMap 里的参数,这个queryMap 其实就是 consumer 的参数,它来自配置的 reference
再看 reference 配置,当 ReferenceConfig 初始化时
// 1
public synchronized void init() {
...
checkAndUpdateSubConfigs();
...
AbstractConfig.appendParameters(map, consumer);
...
}
// 2
public void checkAndUpdateSubConfigs() {
...
checkDefault();
...
}
// 3
public void checkDefault() throws IllegalStateException {
if (consumer == null) {
consumer = ApplicationModel.getConfigManager()
.getDefaultConsumer()
.orElse(new ConsumerConfig());
}
}
// 4
public Optional<ConsumerConfig> getDefaultConsumer() {
List<ConsumerConfig> consumerConfigs = getDefaultConfigs(getConfigsMap(getTagName(ConsumerConfig.class)));
if (CollectionUtils.isNotEmpty(consumerConfigs)) {
return Optional.of(consumerConfigs.get(0));
}
return Optional.empty();
}
上面调用链从 1 到 4,4 中获取了第1个 consumer,这就是我们要找的根源
总结
- 每配置一个 consumer ,无论是从 xml 文件,或是 spring-boot 配置,或是 api 直接创建,都会生成一个 consumerConfig 对象
- 当消费接口,即配置 reference 时,会将 consumer 的参数 merge 过来,如果存在多个 consumer,会挑第一个,当然我们并不知道谁先加载
- 当 reference 存在 consumer 的配置时,注册中心通知的 provider urls 会和 reference 的参数进行合并,合并后生成可调用的 invoker
- 对于 loadbalance 来说,调用时,如果 invokers 非空,则会尝试通过第一个 invoker 的 loadbalance 参数加载负载均衡算法,第一次调用进行加载,后续调用则使用缓存
搜索关注微信公众号"捉虫大师",后端技术分享,架构设计、性能优化、源码阅读、问题排查、踩坑实践。

dubbo 配置 loadbalance 不生效?撸一把源码的更多相关文章
- Dubbo加权轮询负载均衡的源码和Bug,了解一下?
本文是对于Dubbo负载均衡策略之一的加权随机算法的详细分析.从2.6.4版本聊起,该版本在某些情况下存在着比较严重的性能问题.由问题入手,层层深入,了解该算法在Dubbo中的演变过程,读懂它的前世今 ...
- 配置Eclipse可以查看JDK类库源码
一.配置方法 配置Eclipse可以查看JDK类库源码 Window->Preferences->Java->Installed JREs 若没有JRE,需要自己添加进来,有的话,点 ...
- Dubbo一致性哈希负载均衡的源码和Bug,了解一下?
本文是对于Dubbo负载均衡策略之一的一致性哈希负载均衡的详细分析.对源码逐行解读.根据实际运行结果,配以丰富的图片,可能是东半球讲一致性哈希算法在Dubbo中的实现最详细的文章了. 文中所示源码,没 ...
- ubuntu配置android开发环境和编译源码遇到的一些问题
---------------------------------------------环境变量设置--------------------------------------------- 1.设 ...
- 聊聊最近撸Spring源码感悟
一.前言 最近一段时间撸了Spring IOC.AOP.Transactional源码,这篇文章聊聊我写了哪些小玩意,这可能就是阅读源码以后最大收获.希望大家在里面能学习一些什么东西吧: 二. ...
- 使用微软企业库5.0提供的unity配置解藕系统demo(源码)
最近公司集50多号开发人员的人力围绕一个系统做开发,框架是免不了要统一的,公司提供的架构,利于分工合作,便于维护,扩展,升级,其中使用了到微软的企业库来解藕系统,只是因为框架封装,于是在网上学习了一个 ...
- SpringtMVC中配置 <mvc:annotation-driven/> 与 <mvc:default-servlet-handler/> 源码解析
上一篇有提到,当有.无这两个标签时,SpringtMVC 底层所采用的 HandlerMapping 以及 HandlerAdapter 是不一样的.现在就来进行源码调试,揭开 SpringtMVC ...
- MySQL主从复制配置指导及PHP读写分离源码分析
开发环境 master环境:ubuntu16.04.5LTS/i5/8G/500G/64位/mysql5.7.23/php7/apache2 slave环境:kvm虚拟机/ubuntu14.04.01 ...
- C++STL内存配置的设计思想与关键源码分析
说明:我认为要读懂STL中allocator部分的源码,并汲取它的思想,至少以下几点知识你要了解:operator new和operator delete.handler函数以及一点模板知识.否则,下 ...
随机推荐
- 在PHP中使用SPL库中的对象方法进行XML与数组的转换
虽说现在很多的服务提供商都会提供 JSON 接口供我们使用,但是,还是有不少的服务依然必须使用 XML 作为接口格式,这就需要我们来对 XML 格式的数据进行解析转换.而 PHP 中并没有像 json ...
- Java学习之随堂笔记系列——day02
昨天内容回顾1.安装jdk和配置环境变量 配置JAVA_HOME和path,只要配置成功之后就可以直接使用java和javac命令.2.HelloWorld案例3.java的基础语法 注释:给程序的解 ...
- Jmeter压测学习1—第一个项目:登录
我用的是我们公司的测试环境练习的 网址:http://****:9000/login.html 账号是admin 密码:123456 一.打开Jmeter 找到安装目录:bin->Jmeter ...
- Java MD5和SHA256等常用加密算法
前言 我们在做java项目开发的时候,在前后端接口分离模式下,接口信息需要加密处理,做签名认证,还有在用户登录信息密码等也都需要数据加密.信息加密是现在几乎所有项目都需要用到的技术,身份认证.单点登陆 ...
- 启用 Spring-Cloud-OpenFeign 配置可刷新,项目无法启动,我 TM 人傻了(上)
本篇文章涉及底层设计以及原理,以及问题定位,比较深入,篇幅较长,所以拆分成上下两篇: 上:问题简单描述以及 Spring Cloud RefreshScope 的原理 下:当前 spring-clou ...
- 四种引用类型在Springboot中的使用
今天 4ye 来和小伙伴们聊聊这个 强引用,软引用,弱引用,幻象引用(虚引用)啦 嘿嘿,主要是最近读源码的时候经常看到,然后又想到自己第一次知道这个神奇的东西是在 2020-8-21 为啥记得这么清楚 ...
- mysql int(3)与int(10)的数值范围相同吗?
提问: mysql的字段,unsigned int(3), 和unsinged int(6), 能存储的数值范围是否相同.如果不同,分别是多大? 回答: 不同,int(3)最多显示3位无符号整体,in ...
- Mysql读写分离集群的搭建且与MyCat进行整合
1. 概述 老话说的好:不熟悉的东西不要不懂装懂,做人要坦诚,知道就是知道,不知道就是不知道. 言归正传,今天我们来聊聊 Mysql主从读写分离集群是如何搭建的,并且聊一下如何用 MyCat 去访问这 ...
- 零基础怎么学Java?Java的运行机制是什么?Java入门基础!
Java语言是当前流行的一种程序设计语言,因其安全性.平台无关性.性能优异等特点,受到广大编程爱好者的喜爱. 想学习Java语言的同学对于Java的运行机制是必须要了解的!! 计算机高级语言的类型主要 ...
- 洛谷2375 NOI2014动物园(KMP)
题目链接: 题目. 简单一点来说,这个题就是求一个字符串的\(num\)数组的和,其中有\(num[i]\)表示1~i中有多少个不交叉的相等的前缀和后缀 的数目,要求一个\(O(n)\)的做法 QwQ ...