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

说说Pulsar负载均衡的原理和优化

时间:2023-03-22 12:58:48 科技观察

前言前段时间升级Pulsar版本的时候,发现升级后最后一个节点没有流量了。虽然对业务使用没有影响,但负载不均会导致资源浪费。和同事交流后得知,这种情况在之前的升级中也会出现。最后,负载均衡是通过手动调用Pulsar的adminAPI来完成的。我尝试在Google和Pulsar社区中没有发现类似的问题。不知道大家没遇到过还是集群很少升级。我之前所在的公司是走黑Pulsar负载均衡原理的版本。当一个集群可以水平扩展时,负载均衡就显得非常重要了。根本的目的是让每个提供服务的节点平均处理请求,否则扩容困难。这没有意义了。在分析这个问题的原因之前,我们先看一下Pulsar负载均衡的实现。#开启负载均衡器loadBalancerEnabled=true我们可以通过broker的这个配置来控制负载均衡器的切换,默认是开启的。但是使用哪个实现类作为负载均衡器也可以在配置文件中指定:staticLoadManagercreate(finalPulsarServicepulsar){try{finalServiceConfigurationconf=pulsar.getConfiguration();//假设有一个构造函数,其中一个参数是PulsarService。finalObjectloadManagerInstance=Reflections.createInstance(conf.getLoadManagerClassName(),Thread.currentThread().getContextClassLoader());if(loadManagerInstanceinstanceofLoadManager){finalLoadManagercasted=(LoadManager)loadManagerInstance;casted.initialize(脉冲星);返回铸造;}elseif(loadManagerInstanceinstanceofModularLoadManager){finalLoadManagercasted=newModularLoadManagerWrapper((ModularLoadManager)loadManagerInstance);casted.initialize(脉冲星);返回铸造;}}catch(Exceptione){LOG.warn("尝试创建l时出错oadmanager:",e);}//如果创建负载管理器失败,默认为SimpleLoadManagerImpl。returnnewSimpleLoadManagerImpl(pulsar);}broker启动时会从配置文件中读取配置进行加载,如果加载失败会采用SimpleLoadManagerImpl作为自下而上的策略,当broker为集群时,只有leader节点的broker会执行负载均衡器的逻辑,leader选举是通过Zookeeper实现的,静默情况下,broker成为leader的节点每分钟会读取每个broker的数据来判断是否有节点负载过高需要rebalance,判断是否rebalance的依据是由org.apache.pulsar.broker提供的。loadbalance.LoadSheddingStrategy接口,实际上只有一个功能:publicinterfaceLoadSheddingStrategy{/***建议卸载所有返回的bundle。*@return从所有选择的bundle到b的映射他们居住的rokers。*/MultimapfindBundlesForUnloading(LoadDataloadData,ServiceConfigurationconf);}根据所有broker的load信息计算出需要卸载的broker和bundle。这里解释一下unload和bundle的概念:bundle是对一批topic的抽象。bundle和broker关联后,client可以知道应该连接哪个broker;而不是直接将topic绑定到broker,从而实现海量topic的管理。Unload是手动解绑已经绑定到broker的bundle,从而触发负载均衡器选择合适的broker重新绑定;一般在整个集群负载不均时触发。ThresholdShedder原理LoadSheddingStrategy接口目前有三种实现。这里我们以官方默认的ThresholdShedder为例:它的实现算法是根据带宽、内存、流量等各项指标的权重,计算出每个节点的负载值,然后对整个集群计算一个平均值。负载值。#thresholdloadBalancerBrokerThresholdShedderPercentage=10当集群中某个节点的负载值超过平均负载值一定程度(可配置阈值)时,会触发unload。比如上图中,最左边节点红色部分的bundle会被卸载掉,然后重新计算一个合适的broker进行绑定。阈值的目的是为了避免频繁卸载,从而影响客户端的连接。问题产生的原因是这种负载策略确实可以很好的应对一些topic的流量突然爆发的时候,但是当我们的集群升级的时候就不一定了。假设我这里有3个节点:broker0broker1broker2集群升级时,镜像会从broker2->0替换重启,假设升级前每个broker的负载值为10。当broker2重启时,它绑定的bundle被broker0/1接管。当broker1升级时,它绑定的bundle被broker0/2接管。最后升级broker0,它绑定的bundle会被broker1/2接管。只要在此之后没有流量激增触发负载阈值,就会保持当前的负载情况,即broker0一直没有流量。经过我反复测试,确实是这样的现象。./pulsar-perfmonitor-brokers--connect-stringpulsar-test-zookeeper:2181通过这个工具还可以查看各个节点的负载优化方案。目前的ThresholdShedder没有考虑到这种场景,所以我在我们使用的2.10.3版本的基础上,做了一个简单的优化:当原来的逻辑完成后,还没有获取到需要卸载的bundle时,以及还有一个负载很低的broker(emptyBundle),再次触发bundle查询。按照绑定的broker个数排序,选择第一个bundle数量最多的broker进行卸载。修改后打包发布,再走升级流程后,整个集群的负载就均衡了。但实际上,这个解法并不严谨。第二步选择的重点是在负载最高的集群中选择负载最高的bundle;这里只是简单的根据数量来判断,不够准确。就在我准备继续优化的时候,想看看master上有没有人修复这个问题,结果真的有人修复了;但尚未正式发布。https://github.com/apache/pulsar/pull/17456整体思路类似,但是在筛选bundle时需要卸载负载时,是根据bundle的流量本身,这将更准确。综上所述,我们不知道要多久才能看到社区的进展以及这次优化的最终使用,所以我们参考了这个思路,在管理控制台做了一个类似的功能。当升级后负载不均衡时,会手动触发一个逻辑:节点的负载情况计算出负载最高的节点和bundle,并显示在页面上。第二次手动确认是否卸载,确认无误后再卸载。本质上只是将上述优化后的自动加载过程改为手动处理,测试结果是一样的。Pulsar整个项目其实非常庞大,有几十个甚至上百个模块。即使我每次准备发布测试只改一行代码,也要经历编译+Docker镜像打包+上传私服的漫长过程,一般需要1~2个小时;不过总的来说收获还是很大的,最近也提了一些issue和PR,希望以后能更深入的参与社区。