前言
这段时间在做 MQ(Pulsar)相干的治理工作,其中一个局部内容对于音讯队列的降级,比方:
- 一键创立一个测试集群。
- 运行一批测试用例,笼罩咱们线上应用到的性能,并输入测试报告。
- 模仿压测,输入测试后果。
实质目标就是想直到新版本升级过程中和降级后对现有业务是否存在影响。
一键创立集群和执行测试用例比较简单,利用了 helm
和 k8s client
的 SDK 把整个流程串起来即可。
压测
其实略微麻烦一点的是压测,Pulsar
官网自身是有提供一个压测工具;只是性能绝对比拟繁多,只能对某批 topic 极限压测,最初输入测试报告。
最初参考了官网的压测流程,退出了一些实时监控数据,不便剖析整个压测过程中性能的变动。
客户端 timeout
随着压测过程中的压力增大,比方压测工夫和线程数的进步,客户端会抛登程送音讯 timeout 异样。
org.apache.pulsar.client.api.PulsarClientException$TimeoutException:
The producer pulsar-test-212-20 can not send message to the topic persistent://my-tenant/my-ns/perf-topic-0 within given timeout : createdAt 82.964 seconds ago, firstSentAt 8.348 seconds ago, lastSentAt 8.348 seconds ago, retryCount 1
而这个异样在生产业务环境的高峰期偶然也呈现过,这会导致业务数据的失落;所以正好这次被我复现进去后想着剖析下产生的起因以及解决办法。
源码剖析客户端
既然是客户端抛出的异样所以就先看从异样点开始看起,其实整个过程和产生的起因并不简单,如下图:
客户端流程:
- 客户端 producer 发送音讯时先将音讯发往本地的一个 pending 队列。
- 待 broker 解决完(写入 bookkeeper) 返回 ACK 时删除该 pending 队列头的音讯。
- 后盾启动一个定时工作,定期扫描队列头(头部的音讯是最初写入的)的音讯是否曾经过期(过期工夫可配置,默认 30s)。
- 如果曾经过期(头部音讯过期,阐明所有音讯都已过期)则遍历队列内的音讯顺次抛出
PulsarClientException$TimeoutException
异样,最初清空该队列。
服务端 broker 流程:
- 收到音讯后调用 bookkeeper API 写入音讯。
- 写入音讯时同时写入回调函数。
- 写入胜利后执行回调函数,这时会记录一条音讯的写入提早,并告诉客户端 ACK。
- 通过 broker metric 指标
pulsar_broker_publish_latency
能够获取写入提早。
从以上流程能够看出,如果客户端不做兜底措施则在第四步会呈现音讯失落,这类实质上不算是 broker 丢音讯,而是客户端认为过后 broker 的解决能力达到下限,思考到音讯的实时性从而抛弃了还未发送的音讯。
性能剖析
通过上述剖析,特地是 broker 的写入流程得悉,整个写入的次要操作便是写入 bookkeeper,所以 bookkeeper 的写入性能便关系到整个集群的写入性能。
极其状况下,假如不思考网络的损耗,如果 bookkeeper
的写入提早是 0ms,那整个集群的写入性能简直就是无下限;所以咱们重点看看在压测过程中 bookkeeper
的各项指标。
CPU
首先是 CPU:
从图中能够看到压测过程中 CPU 是有显著增高的,所以咱们须要找到压测过程中 bookkeeper 的 CPU 大部分损耗在哪里?
这里不得不吹一波阿里的 arthas 工具,能够十分不便的帮咱们生成火焰图。
剖析火焰图最简略的一个办法便是查看顶部最宽的函数是哪个,它大概率就是性能的瓶颈。
在这个图中的顶部并没有显著很宽的函数,大家都差不多,所以并没有显著损耗 CPU 的函数。
此时在借助云厂商的监控得悉并没有失去 CPU 的下限(limit 限度为 8 核)。
应用 arthas 过程中也有个小坑,在 k8s 环境中有可能利用启动后没有胜利在磁盘写入 pid,导致查问不到 Java 过程。
$ java -jar arthas-boot.jar
[INFO] arthas-boot version: 3.6.7
[INFO] Can not find java process. Try to pass <pid> in command line.
Please select an available pid.
此时能够间接 ps
拿到过程 ID
,而后在启动的时候间接传入 pid
即可。
$ java -jar arthas-boot.jar 1
通常状况下这个 pid
是 1。
磁盘
既然 CPU 没有问题,那就再看看磁盘是不是瓶颈;
能够看到压测时的 IO 等待时间显著是比日常申请高许多,为了最终确认是否是磁盘的问题,再将磁盘类型换为 SSD 进行测试。
果然即使是压测,SSD 磁盘的 IO 也比一般硬盘的失常申请期间提早更低。
既然磁盘 IO 提早升高了,依据前文的剖析实践上整个集群的性能应该会有显著的回升,因而比照了降级前后的音讯 TPS 写入指标:
降级后每秒的写入速率由 40k 涨到 80k 左右,简直是翻了一倍(果然用钱是最快解决问题的形式);
但即使是这样,极限压测后仍然会呈现客户端 timeout,这是因为无论怎么进步服务端的解决性能,仍然没法做到没有提早的写入,各个环节都会有损耗。
降级过程中的 timeout
还有一个要害的步骤必须要笼罩:模仿生产现场有着大量的生产者和消费者接入收发音讯时进行集群降级,对客户端业务的影响。
依据官网举荐的降级步骤,流程如下:
- Upgrade Zookeeper.
- Disable autorecovery.
- Upgrade Bookkeeper.
- Upgrade Broker.
- Upgrade Proxy.
- Enable autorecovery.
其中最要害的是降级 Broker 和 Proxy,因为这两个是客户端间接交互的组件。
实质上降级的过程就是优雅停机,而后应用新版本的 docker 启动;所以客户端肯定会感知到 Broker 下线后进行重连,如果能疾速主动重连那对客户端简直没有影响。
在我的测试过程中,2000 左右的 producer 以 1k 的发送速率进行音讯发送,在 30min 内实现所有组件降级,整个过程客户端会主动疾速重连,并不会出现异常以及音讯失落。
而一旦发送频率减少时,在重启 Broker 的过程中便会呈现上文提到的 timeout 异样;初步看起来是在默认的 30s 工夫内没有重连胜利,导致积压的音讯曾经超时。
通过剖析源码发现要害的步骤如下:
客户端在与 Broker 的长连贯状态断开后会主动重连,而重连到具体哪台 Broker 节点是由 LookUpService
解决的,它会依据应用的 topic 获取到它的元数据。
实践上这个过程如果足够快,对客户端就会越无感。
在元数据中蕴含有该 topic 所属的 bundle 所绑定的 Broker 的具体 IP+ 端口,这样能力从新连贯而后发送音讯。
bundle 是一批 topic 的形象,用来将一批 topic 与 Broker 绑定。
而在一个 Broker 停机的时会主动卸载它所有的 bundle,并由负载均衡器主动划分到在线的 Broker 中,交由他们解决。
这里会有两种状况升高 LookUpSerive 获取元数据的速度:
因为所有的 Broker 都是 stateful 有状态节点,所以降级时是从新的节点开始降级,假如是 broker-5
,假如降级的那个节点的 bundle 切好被转移 broker-4
中,客户端此时便会主动重连到 4 这个 Broker 中。
此时客户端正在讲沉积的音讯进行重发,而下一个降级的节点正好是 4,那客户端又得期待 bundle 胜利 unload 到新的节点,如果恰好是 3 的话那又得套娃了,这样整个音讯的重发流程就会被拉长,直到超过等待时间便会超时。
还有一种状况是 bundle 的数量比拟多,导致下面讲到的 unload 时更新元数据到 zookeeper 的工夫也会减少。
所以我在思考 Broker 在降级过程中时,是否能够将 unload 的 bundle 优先与
Broker-0
进行绑定,最初全副降级胜利后再做一次负载平衡,尽量减少客户端重连的机会。
解决方案
如果咱们想要解决这个 timeout 的异样,也有以下几个计划:
- 将 bookkeeper 的磁盘换为写入时延更低的 SSD,进步单节点性能。
- 减少 bookkeeper 节点,不过因为 bookkeeper 是有状态的,程度扩容起来比拟麻烦,而且一旦扩容再想缩容也比拟艰难。
- 减少客户端写入的超时工夫,这个能够配置。
- 客户端做好兜底措施,捕捉异样、记录日志、或者入库都能够,后续进行音讯重发。
- 为 bookkeeper 的写入提早减少报警。
- Spring 官网刚出炉的 Pulsar-starter 曾经内置了 producer 相干的 metrics,客户端也能够对这个进行监控报警。
以上最好实现的就是第四步了,成果好成本低,举荐还没有实现的都尽快 try catch
起来。
整个测试流程消耗了我一两周的工夫,也是第一次全方位的对一款中间件进行测试,其中也学到了不少货色;不论是源码还是架构都对 Pulsar
有了更深刻的了解。