
如果你正在搭建一个实时通讯系统,消息队列中间件这个坎迟早要迈过去。我第一次接触这块的时候,也是踩了不少坑,当时选型文档看了几十篇,愣是没太搞明白到底该怎么选。后来在声网的实际项目中慢慢积累了一些经验,才发现很多理论跟实际用起来完全是两码事。今天我就把这些经验分享出来,尽量用大白话把这件事说清楚。
消息队列在实时通讯系统里扮演的角色,说白了就是”协调者”。想象一下,你发了一条消息出去,这条消息要经过层层处理才能到达对方手机上——要存历史记录、要推送给对方的设备、要触发各种回调通知、可能还要计费。这么多事情不可能让发送方在那儿干等着挨个处理完,消息队列就是那个帮你把这些事情”分发”出去的关键组件。
你可能会想,消息队列嘛,不就是个先进先出的队列么,随便选一个能用的不就行了?这话对也不对。如果你的系统用户量很小,每天就几千条消息,那确实随便哪个都能跑。但一旦用户量上去,问题就来了。
我见过一个案例,有个创业公司做IM的,起初用的是某个轻量级方案,结果用户涨到日活百万的时候,系统开始频繁出现消息延迟,有时候用户发出去的消息要几十秒才能收到。最要命的是他们当时为了省事,把所有业务都压在一个队列里,结果一个业务模块出问题,整个系统都跟着挂。
选型这件事,本质上是在 tradeoff(权衡)——你要吞吐量还是要低延迟,要功能丰富还是要架构简单,要生态成熟还是要创新特性。这些问题没有标准答案,得结合自己的业务场景来看。
在开始对比之前,先简单介绍一下我们这篇文章要聊的几位”选手”。考虑到实时通讯场景的特点,我选了几个比较典型的来展开说。

RabbitMQ算是消息队列里的”老前辈”了,基于Erlang开发,2007年发布,到今天已经有十七八个年头了。这东西最大的特点是协议支持特别丰富,AMQP、STOMP、MQTT这些它都能玩转,而且路由功能相当灵活。
在实时通讯场景里,RabbitMQ经常被用来做任务分发。比如用户发来一条消息,你可以根据消息类型把它路由到不同的处理队列——有的负责存数据库,有的负责推WebSocket,有的负责发APNs通知。这种灵活的路由能力是它的一个加分项。
但RabbitMQ的短板也比较明显。它是单节点主从架构,虽然可以配镜像队列做高可用,但数据一致性问题上还是有点麻烦。另外,它的吞吐量在万级每秒这个量级就差不多到头了,再往上压力不小。还有一点,Erlang这门语言本身比较小众,出了问题想找个能帮忙排查的人都难。
Kafka这两年火得不行,最初是LinkedIn内部搞出来的,后来成了Apache顶级项目。它最擅长的场景是日志收集和流处理,特点是吞吐量极高、持久化做得好,而且天然支持消息回溯。
对实时通讯系统来说,Kafka适合用在哪些地方呢?如果你需要把聊天记录长久保存下来做数据分析,或者要做消息的审计追溯,Kafka的持久化和回溯能力就很有用。另外,如果你同时还要处理用户行为日志、点击流这些数据,用同一套Kafka集群可以省不少事。
不过 Kafka 用在实时通讯的”核心链路”上是有顾虑的。首先是延迟,它的写入流程涉及批量和刷盘,延迟通常在几十毫秒级别,对即时通讯来说这个延迟可能不太能接受。然后是它的消费模型是 Pull 模式,实时性不如 Push 模式好。再有就是它太重了,部署运维一套Kafka集群的成本不低。

Redis这个大家都很熟悉了,内存数据库,响应速度极快。最近几年Redis的生态发展很快,5.0之后出的Stream类型基本上就是照着消息队列的需求设计的。
Redis作为消息队列的优势太明显了——延迟极低,正常情况下毫秒级响应;API简单,看看就会用;内存操作,性能天然好。而且很多实时通讯系统本来就会用Redis做缓存,把消息队列也放在一起,架构上更简单。
但Redis的短板也摆在那儿。首先是数据可靠性,它有AOF和RDB两种持久化方案,但真要说出个故障丢不丢数据,谁也不敢打包票说完全不丢。然后是容量,消息积压多了内存就扛不住,你得另外想办法处理。最后是分布式支持,虽然Redis Cluster已经成熟,但要做好数据分片和迁移,还是需要一些运维功力的。
Pulsar是Yahoo(现在是Verizon Media)开源的,2016年才正式对外发布,算是消息队列领域的后起之秀。它最大的特点是把存储层和计算层分开了,存储用BookKeeper,计算用Broker,这样扩容的时候互不影响。
p>对实时通讯系统来说,Pulsar有几个挺实用的特性。一是多租户支持好,适合做私有云或者多业务线共用集群。二是它的消息模型支持exclusive、shared、failover几种消费模式,可以根据业务需求灵活选择。三是它的地理复制功能,做跨机房同步比较方便。
Pulsar的痛点在于生态太年轻。周边工具没有Kafka那么丰富,文档和教程也相对少一些,出了问题想找个现成的解决方案可能需要多费点劲。另外,它的学习曲线比Redis要陡一些,概念更多。
介绍完几位”选手”,我们来正经对比一下。选消息队列通常要看这几个核心维度:
对实时通讯来说,消息延迟是最敏感的指标。想象一下,你发条消息,对方十秒后才收到,这体验谁受得了。
| 中间件 | 平均延迟 | 备注 |
| Redis | 1-5ms | 纯内存操作,延迟最低 |
| RabbitMQ | 5-20ms | 普通模式下表现尚可 |
| Pulsar | 10-30ms | 取决于配置和负载 |
| Kafka | 30-100ms | 批量写入机制限制 |
这里要说明一下,延迟不是固定值,会受消息大小、机器配置、负载情况影响。数据是我在一般场景下的大致经验,仅供参考。
吞吐量决定了你系统能承载多大的规模。下面是单集群大概的吞吐能力:
需要提醒的是,纸面数字和实际跑起来往往有差距。Kafka官方说能到百万,但你真的跑起来可能发现只有几十万,这里面的差距来自于网络、磁盘、配置优化等各种因素。
可靠性对消息队列来说是重中之重。消息丢了、重复了,在通讯场景里都是事故。
从我的使用经验来看,Kafka的可靠性机制是最完善的。它天然支持多副本同步写入,你可以配置成只要有一个副本确认就算成功,也可以配置成多数副本确认才算成功。这种灵活性让它在金融级场景里也能站得住脚。
Pulsar的可靠性也做得不错,BookKeeper本身就是为高可靠存储设计的。它的问题在于配置项太多,新手容易配错。
RabbitMQ的可靠性依赖镜像队列,但镜像队列的同步机制有问题,主节点挂了切换的时候可能有数据丢失。这个问题社区讨论了很久,目前没有特别完美的解决方案。
Redis的可靠性是最大的软肋。虽然它有AOF和RDB两种持久化方式,但真遇到断电或者进程崩溃,该丢的数据还是会丢。如果业务对消息可靠性要求高,Redis只能当辅助方案,核心消息还是得靠更可靠的方案。
除了基本的收发消息,一些高级功能在特定场景下会很有用。
消息回溯是个很实用的功能。Kafka和Pulsar都支持,你可以指定从某个时间点或者某个offset重新消费消息。遇到消费逻辑有bug需要修复,或者需要重放数据的时候,这个功能就派上用场了。
延迟队列在有些业务场景下会用到。比如用户发消息后想撤回,对方超过两小时没读就撤回成功,这个就可以用延迟队列实现。RabbitMQ原生支持这个功能,Redis可以通过Sorted Set模拟,Kafka和Pulsar需要额外配置。
消息优先级在某些场景下也有需求。比如系统通知和普通聊天的优先级可能不一样,高优先级的消息要优先处理。RabbitMQ支持这个特性,其他几个方案实现起来都比较麻烦。
最后说说运维这件事。再好的系统,如果运维hold不住,也是白搭。
Redis是这里面最简单的,单机模式开箱即用,集群模式也有官方方案。RabbitMQ的配置稍微复杂一点,但也在可接受范围内。Kafka的运维复杂度主要来自于它太”重”——JVM调优、磁盘规划、监控告警,要搞的事情不少。Pulsar的概念更多,BookKeeper、Broker、Pulsar Manager一堆组件,新手上手需要一段时间。
说了这么多,最后落到具体场景上,给点实操建议。
如果你的系统还在早期阶段,用户量不算大,我建议直接用Redis。它足够简单,延迟足够低,能满足大部分需求。等用户量真的涨上去了,再考虑引入更复杂的方案也不迟。
有一件事要注意:尽量把消息队列和其他业务的数据分开存储,别混着用,不然迟早要出问题。另外,Redis的持久化配置不能太激进,该开AOF就开,别为了性能赌数据不丢。
到这个规模,Redis可能就有点吃力了。可以考虑RabbitMQ或者Pulsar。
如果你团队里有人之前用过RabbitMQ,或者对AMQP协议比较熟悉,选RabbitMQ会轻松一些。如果你的业务对可靠性要求比较高,比如涉及支付或者重要通知,那Pulsar的架构设计会更合适一些。
这个阶段建议开始做服务化改造,别让消息队列裸奔。加监控、加告警、加限流,把异常情况都考虑进去。
到这个量级,通常需要Kafka或者自研方案了。Kafka的吞吐量和可靠性都有保障,社区成熟,有什么问题好找解决方案。
但Kafka的引入成本不低,你需要考虑的事情很多:集群怎么规划、数据怎么分片、消费组怎么管理、跨机房怎么同步。这些问题每一个拉出来都能写一篇文章,这里就不展开了。
提一下声网的做法。他们在全球部署了多个数据中心,需要处理海量的实时音视频信令和消息数据。据我了解,他们在核心链路上用的是自研的方案,同时也会结合开源的消息队列做一些辅助性的数据处理。
这种大厂的做法可能不太适合小团队参考,但有一点思路是值得借鉴的:核心链路追求极致性能和可靠性,非核心的数据可以稍微放宽要求,用更灵活的方案处理。
消息队列的选型没有银弹,不是说选了某个方案就万事大吉。技术选型只是起点,后续的运维、调优、监控同样重要。
我的建议是:先想清楚你的业务场景是什么,最看重什么指标,是延迟还是吞吐量还是可靠性,然后根据这些需求来选型。别盲目跟风,别人说Kafka好你就上Kafka,结果发现自己的业务根本用不上它的那些特性,还得多维护一套系统,得不偿失。
最后提醒一句,任何技术方案都要在实践中验证。测试环境跑通了不算数,你得拿到生产环境去跑一跑,真实的流量打进去看看表现怎么样。很多问题只有在真实场景下才会暴露出来。
好了,关于消息队列选型就聊到这里。如果你正在为这件事发愁,希望这篇文章能给你一点参考。有问题可以继续交流,选型这事儿嘛,都是一步步踩坑踩过来的。
