Skip to content

消息模式综述 ​

竞争消费者、发布订阅、RPC over MQ,这三个模式回答的是同一个问题,一条消息发出去之后该被谁消费。想清楚「谁消费」,消息系统的拓扑就清楚了一大半。

1. 竞争消费者 ​

多个消费者监听同一个队列,每条消息只投给其中一个。它的作用是负载均衡,把处理压力摊到多台实例上。

txt
                    ┌─ Consumer 1
Queue ──轮询分发─────┼─ Consumer 2
                    └─ Consumer 3

分配策略默认是轮询,但真正决定「谁拿多少」的是 Prefetch。Prefetch 等于 0(默认值)时无限制预取,Broker 按轮询依次分发消息,不关心消费者是否处理完,处理快的消费者早早空闲;Prefetch 等于 1 时变成公平分发,处理完一条才推下一条,慢的少拿、快的多拿。

java
channel.basicQos(1);  // 公平分发

竞争消费者是消息系统最常用的模式,它的容错是天然的。一个消费者崩溃,未确认的消息重新入队,被其他消费者接手。代价是顺序不保证,同一队列的多条消息可能被不同消费者同时处理,先后就乱了。要顺序,只能降级为单个消费者,或者用消息哈希路由到固定队列,见 消息顺序性。

2. 发布订阅,一条消息被所有订阅者处理 ​

和竞争消费者相反,发布订阅把一条消息广播给所有订阅者。实现靠 Fanout Exchange,每个订阅者有自己的队列,消息复制到每个队列。

txt
Producer ──▶ Fanout Exchange ──▶ Queue A ──▶ Consumer A
                              ──▶ Queue B ──▶ Consumer B

竞争消费者和发布订阅的区别只在一件事,一条消息被消费几次。竞争消费者只消费一次,发布订阅每个订阅者各消费一次。负载均衡用前者,事件广播用后者。

典型场景是用户注册后要发短信、发邮件、加积分、写日志,这些动作各自独立、互不影响,一条注册事件广播出去,各订阅者各干各的。

3. RPC over MQ,通常不该用的模式 ​

RPC over MQ 用消息队列实现请求响应,客户端发请求消息,服务端处理后把结果发回回调队列,靠 correlationId 匹配请求和响应。

txt
Client ──请求──▶ request.queue ──▶ Server
   │                                │
   └──响应── callback.queue ◀────────┘

它和 HTTP 调用的差别在于中间隔了一层 Broker,天然异步、解耦、可负载均衡,但延迟更高、复杂度更高,要自己处理 correlationId 匹配和超时。

关键的判断是,如果只是简单的同步调用,用 HTTP 或 gRPC。RPC over MQ 只在两种情况下才值得考虑,一是已经深度依赖 RabbitMQ 且不想引入第二套调用链路,二是调用方和被调用方之间需要队列解耦。绝大多数场景下它是反模式,不要因为「消息队列能传返回值」就硬把它当 RPC 框架用。

4. 怎么选 ​

需求模式
一条消息被一个消费者处理,分担负载竞争消费者
一条消息被所有订阅者各自处理发布订阅
同步请求响应HTTP / gRPC,不是 MQ

判断的根子就一条,消息的消费者是「一」还是「多」。负载均衡选竞争消费者,事件广播选发布订阅,需要请求响应用 HTTP。RPC over MQ 卡在中间,两头都不占优,大多数时候不选它。

Released under the MIT License.