发布订阅(Pub/Sub)
对于消息队列传统的模式来说,一个消费者消费一条消息,这条消息被消费之后就不会再次被其它的消费者消费。但是在发布订阅模式中,一条消息是可以被多个消费者消费的,这些消费者其实相当于是订阅了这条队列的消息。当有新的消息出现在队列中,就会像广播一样让所有订阅者都获得这条消息。
为什么要使用发布订阅模式
- 解耦和异步通信: 发布订阅模式允许发布者(发布消息的一方)和订阅者(接收消息的一方)之间解耦。发布者不需要知道哪些订阅者会接收消息,订阅者也不需要了解消息的来源。这种解耦使系统更加灵活和可扩展。
- 实时数据处理和通知: 当需要实时传输数据并且多个接收者需要收到同一数据时,发布订阅模式特别有用。例如,即时聊天应用程序中的消息传输,或者实时数据分析系统中的数据处理和通知。
- 事件驱动架构: 在事件驱动架构中,发布订阅模式是核心机制之一。系统中的各个组件可以通过发布和订阅事件来响应特定的业务事件,从而使系统更加响应式和可维护。
- 分布式系统协调: 在分布式系统中,不同节点之间可能需要进行协调和通信。通过发布订阅模式,可以实现跨节点的消息传递和事件处理,促进系统间的解耦和灵活性。
- 解决竞态条件: 在多线程或多进程环境中,使用发布订阅模式可以避免竞态条件的发生。订阅者能够按照自己的速度和时间处理接收到的消息,不会因为速度不同而导致数据不一致或丢失。
举个例子
比如现在有一个订单系统,在用户下单以后,我们需要同步给用户发送下单成功的通知,同时也需要给商家发送用户已经下单的通知;
如果使用传统的模式,我们大概需要一个事务隔离的环境执行如下逻辑
- 用户成功下单
- 给用户发送短信、站内消息等
- 给商家发送有用户下单短信、站内消息等
如果使用发布/订阅模式的话则可以拆成两个部分;
- 发布者
- 用户成功下单
- 发布者发布消息 publish
- 订阅者
- 订阅者一,发送消息
- 订阅者二,发送消息
RabbitMQ实现
在 RabbitMQ 中,交换机(Exchange)是消息的分发中心,它决定了消息应该被发送到哪些队列。RabbitMQ 提供了几种不同的交换机类型,每种类型都有不同的消息分发规则,其中包括了发布订阅模式的实现方式。
其中 Fanout Exchange (扇出交换机)
它会把所有发送到它的消息广播到所有与它绑定的队列中。这种模式实现了典型的发布订阅(Publish-Subscribe)模式,其中:
- 发布者将消息发送到 Fanout Exchange。
- Fanout Exchange 接收到消息后,会将消息复制并发送到所有与之绑定的队列。
- 订阅者分别从各自的队列中接收消息。
发布者代码(创建订单)
// 发布订单创建
public function orderCreate(){
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
// 定义交换机
$channel->exchange_declare('orders', 'fanout', false, false, false);
$data = '订单号:' . time();
$msg = new AMQPMessage($data);
// 注意,这里是指定的交换机,第三个参数还是队列名,之前普通队列我们指定的是第三个参数
$channel->basic_publish($msg, 'orders');
echo