当前位置: 首页 > 科技观察

Dubbo配置Loadbalance没有生效?拍个源码

时间:2023-03-14 13:28:49 科技观察

文章结束本文转载自微信公众号《捉虫达人》,作者为捉虫达人。转载本文请联系捕虫大师公众号.背景很久以前,我写了一个dubboloadbalance的扩展,供业务端使用(为了描述方便,这个loadbalance扩展就叫XLB)。这两天业务方反映XLB没有生效。我心想,这不可能,而且我用了半年多了~去查,于是就登上了没有生效的消费机去查。幸运的是,我救了一只手。当加载XLB时,它会打印一行日志。看服务,没有打印日志,说明没有加载XLB。成功后问对应的开发,你们是按照我的文档配置loadbalance的吗?回复:按照文档配置。现在不相信,但转念一想,配置loadbalance这么简单,应该不会出错。我的文档和他的application都在xml文件中配置了consumerloadbalance抱着试一试的态度拉了一下他们项目的代码,发现配置确实如上,但是我发现他们的application.properties配置文件也配置了一个consumer属性dubbo.consumer.check=false根据多年和dubbo打交道的经验,这里有问题,经代码确认,确实xml和application.properties都是loaded那么这里可能有问题。dubbo从xml加载生成消费者配置,dubbo-springboot-starter从application.properties加载配置生成消费者配置。这不是冲突吗?不要只看dubbo.consumer的配置.check,它其实会生成一个完整的consumer配置,不过loadbalance是默认值。为什么业务方要这样配置呢?大概率是因为我的文档只给出了xml形式的配置,没有给出spring-boot的配置,他们本来是用spring-boot的配置方式,然后看到我的文档是xml,结果不会配置。他们还写了一个xml,跟原来的配置有冲突。为了验证是不是这个问题引起的,我把他的application.properties的dubbo.consumer.check配置移到了xml文件中,果然重启后加载到XLB中然后我在本地测试应用做了这样的验证:两组配置相同,只是顺序不同。测试结果,case1可以加载到XLB,case2不能,所以我猜测,配置dubboconsumer后加载的源码明显不符合我的风格。打开下面的源代码。如果你不感兴趣,你可以跳过它。底部有总结。首先,找出何时加载loadbalance。在AbstractClusterInvoker的invoke方法中,加载了loadbalance@。OverridepublicResultinvoke(finalInvocationinvocation)throwsRpcException{...List>invokers=list(invocation);LoadBalanceloadbalance=initLoadBalance(invokers,invocation);RpcUtils.attachInvocationIdIfAsync(getUrl(),invocation);returndoInvoke();}加载代码如下:获取方法参数r(RpcUtils.getMethodName(invocation),LOADBALANCE_KEY,DEFAULT_LOADBALANCE));}else{returnExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(DEFAULT_LOADBALANCE);}}使用缓存加载扩展publicTgetExtension(Stringname){if(StringUtils.isEmpty(name)){thrownewIllegalArgumentException("Extensionname==null");}if("true".equals(name)){returngetDefaultExtension();}finalHolderholder=getOrCreateHolder(name);Objectinstance=holder.get();如果(实例==null){同步(持有人){instance=holder.get();if(instance==null){instance=createExtension(名称);holder.set(实例);}}}返回(T)instance;}可以看出,在发起dubbo调用的时候就启动了loadbalance,当invokers不为空(即provider不为空)时,会进行初始化,后续的loadbalance会取自缓存。负载均衡根据第一个调用者的负载均衡参数确定。所以问题转移到调用者的负载平衡从何而来?provider不会配置loadbalance,所以这个参数必须从consumer的配置中获取。MergeUrl在RegistryDirectory的toInvokers方法中调用,由注册中心通知。调用,就是当你从注册中心拿到providerurl的时候,你要合并能不能用,merge的内容是什么?privateURLmergeUrl(URLproviderUrl){//1.mergeconsumer参数providerUrl=ClusterUtils.mergeUrl(providerUrl,queryMap);//2.mergeconfigurator参数providerUrl=overrideWithConfigurator(providerUrl);...returnproviderUrl;}在1中,queryMap中的参数是合并。这个queryMap其实就是消费者的参数。它来自配置参考,然后查看参考配置。ReferenceConfig初始化时,publicsynchronizedvoidinit(){...checkAndUpdateSubConfigs();...AbstractConfig.appendParameters(map,consumer);...}//2publicvoidcheckAndUpdateSubConfigs(){...checkDefault();...}//3publicvoidcheckDefault()throwsIllegalStateException{if(consumer==null){consumer=ApplicationModel.getConfigManager().getDefaultConsumer().orElse(newConsumerConfig());}}//4publicOptionalgetDefaultConsumer(){ListconsumerConfigs=getDefaultConfigs(getConfigsMap(getTagName(ConsumerConfig.class)));if(CollectionUtils.isNotEmpty(consumerConfigs)){returnOptional.of(consumerConfigs.get(0));}returnOptional.empty();}以上调用链从1到4拿到第一个consumer,4,这就是我们要找的rootcause当一个consumerConfig对象消费一个接口的时候,也就是配置一个引用的时候,会合并这个consumer的参数。如果有多个消费者,它会选择第一个。当然,当引用存在时,我们并不知道谁先加载消费者的配置。通知的提供者url将与引用的参数合并,合并后将生成一个可调用的调用程序。对于loadbalance,如果invokers不为空,会尝试通过第一个invoker的loadbalance参数加载负载均衡算法。第一次调用加载,后续调用使用缓存