Pulsar
收录于 工程治理体系
业务一复杂,Pulsar 的高吞吐就不再只是调参数
高吞吐不是压测图上的一个数字。真正把问题逼出来的,是同一套系统里开始混跑不同业务、不同顺序要求和不同处理耗时。
很多团队一聊消息系统的性能,第一反应都是吞吐量:一秒能写多少、一天能跑多少、批量开关开不开、参数还能不能再拧一拧。
这种讨论当然不算错,但它很容易越聊越窄,最后只剩一堆参数名:batching、linger、publish delay、partition。
在业务还比较单一的时候,这样聊问题不大。
消息类型少,处理链路也短,下游耗时也比较接近。这个阶段只要批处理做得合理,分区数和消费并发没有明显配错,吞吐往上走通常是自然结果。
但业务一复杂,事情就不是这样了。
比如同一套系统里,开始同时跑订单状态、库存扣减、营销触达这几类消息。它们的顺序要求、处理耗时、失败代价都不一样:有的要求按 key 保序,有的更在意并发;有的只是简单落库,有的要打多个下游;有的失败以后可以稍后重试,有的一旦重复处理就会带来业务副作用。这个时候再只盯 Producer 一次发多少,通常就不够了。
因为真正把高吞吐问题逼出来的,往往不是压测本身,而是业务已经把后面那串问题一起带出来了:消息怎么分发,失败以后怎么确认和重投,下游吃不动时积压会怎么长,系统又该怎么看自己是不是开始失衡。
业务要的也不是一个漂亮的 TPS,而是这些消息代表的结果还能不能在可接受的时间里稳定兑现。
Pulsar 值得讨论,我觉得也在这里。
不是因为它有一组更好看的参数,而是因为它没有把高吞吐停在发送侧,而是继续往后处理。
一、业务还单一时,参数视角通常够用
高吞吐系统当然离不开 batch。
逐条发、逐条存、逐条处理,成本一定会很高。流量一上来,协议开销、系统调用、网络往返、Broker 处理成本都会被放大。只从这一层看,batch 先解决的是固定成本摊薄,这没什么问题。
所以在业务比较单一的时候,团队先去看 Producer 怎么攒批、Broker 怎么少处理几次请求,这很正常。
因为这个阶段消息的处理方式差异没那么大,链路后半段也没那么容易互相拖累。
真正开始变难,通常是在系统从“承接一类差不多的消息”走到“开始混跑几类差异很大的业务消息”之后。
这时候团队真正卡住的,往往不是“还能不能再多打一批”,而是下面这些事:
- 一类消息要求按 key 顺序处理,另一类消息希望尽量并发,系统能不能同时接住。
- 同一批消息里只有一部分处理成功时,系统是整批重投,还是能记住已经处理到哪里。
- 库存、风控这类慢消息混进来时,会不会把原本很快的通知类消息一起拖住。
- 下游跟不上时,系统暴露出来的是短暂抖动,还是 backlog 会越堆越大。
这些问题都不是参数表能直接回答的。
它们开始对应的是业务结果:有些消息会不会变晚,有些结果会不会迟迟不兑现,有些失败会不会因为重投方式不对变得越来越难收。
二、Pulsar 真正往前做的,是把 batch 带进后面的处理链路
如果只看发送侧,很多消息系统都会做 batch。
Pulsar 更值得看的一点,是它没有把 batch 理解成“Producer 先攒一包,发出去就算完事”。官方文档把这件事说得很直白:batch 在 Broker 侧会按批次单元被跟踪和存储,到了消费阶段再拆回单条消息。
这意味着 batch 不是网卡前面的一次性技巧,而是继续进入了后面的消息处理链路。
这样做的意义,不只是吞吐数字更好看,而是后面的语义还有机会继续保住。
比如按 key 分发。
如果业务要求同一个 key 的消息尽量按顺序处理,那 batch 就不能只是随手拼。前面怎么拼,后面就会影响路由和顺序。Pulsar 官方文档专门把 Key_Shared 下的 key-based batching 单独拎出来讲,原因也很现实:默认 batch 可能会破坏这类分发语义。
这件事的价值不在于多了一个配置项,而在于它承认了一个现实:吞吐优化不能把分发语义直接压坏。
再比如确认。
业务处理很少是整齐划一的。一批消息里,有的已经成功落库了,有的卡在下游调用,有的可能因为数据问题根本过不去。如果系统只能整批确认、整批重投,后面的治理会很快变粗。Pulsar 文档里把 batch index acknowledgment 讲得很明确:默认情况下,批里没全部确认时可能发生整批重投;开启 batch index ack 以后,Broker 会跟踪批内索引的确认状态。
本质上它是在补这件事:消息可以按批跑,但系统还是得尽量记住批里哪些消息已经处理过,这样后面的重试、补偿和排查才不会一锅端。
消费侧也一样。
如果下游本来就是按批处理的,那 Consumer 最关心的不是 Producer 那边怎么攒,而是自己希望按多少条、多少字节、等多久再拿一批消息。Pulsar 的 BatchReceivePolicy 就是在消费侧回答这个问题:批量接收什么时候结束,由消息条数、字节数和等待时间共同决定。
这样 batch 就不再只是发送端私下做的优化,而是消费端也能明确表达处理节奏。
所以我会觉得,Pulsar 真正值得聊的,不是“它也支持 batch”,而是它把 batch 后面那几件业务上迟早会撞上的事,一起摆到了系统里。
三、到了工程现场,先坏掉的通常不是写入峰值,而是消费闭环
写入峰值当然要看,但它通常不是最早暴露问题的地方。
Producer 打得很快,Broker 先接住,这件事本身并不难做出漂亮数字。真正容易出问题的,往往是消费侧和后面的积压。
原因也不复杂。
业务处理的耗时本来就不均匀,有的消息只是简单落库,有的消息要查多个依赖,有的消息失败以后还要补偿。只要这些东西混在一套承载面里,系统表面上可能还在高吞吐,实际上 backlog、重投和延迟已经慢慢长出来了。业务侧看到的结果通常也不是“系统吞吐下降”,而是某一类结果开始变慢、变晚,甚至一直没有兑现。
Pulsar 的订阅模型之所以值得看,也是在这里。
一旦用了 Shared 或 Key_Shared,很多问题都会变得非常具体:ACK 粒度怎么选,失败消息怎么回投,并发拉高以后顺序损失多少,某一类消息处理很慢时 backlog 会不会把别的消息一起拖住。
所以到了工程上,更有用的通常不是“系统一天跑了多少消息”这个总量,而是下面这些信号:
- 入流是不是稳定,写入延迟有没有开始抖。
- 出流是不是跟得上,还是只是把流量不断堆进系统里。
- backlog 有没有持续增大,最老未确认消息挂了多久。
- 延迟、重投和积压是不是一起上来了。
比 backlog 总量更有用的,往往是最老那批未确认消息已经卡了多久。
总量大不一定危险,但如果最老消息的滞留时间一直往上爬,通常说明系统已经不是单纯“忙”,而是开始失衡了。这个时候再看吞吐,就不能只看峰值,得看这个峰值是不是拿更长等待时间、更粗的确认粒度或者更重的消费侧代价换来的。
对业务来说,这种失衡最后会表现成结果兑现变慢,而不是监控图上某条线不好看。
四、真正该看的,不是跑分,而是后面的消息处理闭环
如果业务里已经出现了混跑、顺序要求、部分失败、慢消费者这些问题,那你讨论的就不再是单点性能,而是一整套消息处理方式。
我现在更愿意从这个角度看 Pulsar。原因不复杂:在它这里,下面这些事不是分散在不同层里各自处理,而是会被连在一起看:
- batch 不只是发送端优化,还会继续影响路由、确认和消费。
- 吞吐不只看 Producer,还要看消费闭环能不能成立。
- 性能不只看总量,还要看系统是不是开始慢慢失衡。
最后想落下来的其实就一句话。
业务一旦复杂,高吞吐就不再只是参数问题。它后面连着分发、确认、消费、重投和观测。Pulsar 值得讨论,也不是因为它把某个数字做得更大,而是因为它把这些原本迟早要补的工程问题放到了同一条链路里。
反过来说,如果系统现在承接的还是一类处理方式比较接近的消息,参数优化当然仍然重要,甚至已经够用。
真正值得把问题讲重,是在同一套承载面已经开始混跑不同业务、不同顺序要求和不同失败代价之后。
如果你更关心消费侧承载面是怎么被慢消息拖重的,后面我会单独展开负载均衡这一层是怎么被重新定义的。
如果你想把确认语义和隔离边界拆开看,后面我也会单独把 ACK 解决什么、解决不了什么讲透。