RabbitMQ之发布/订阅(Publish/Subscribe)模式(PHP版)

发布/订阅模式(Publish/Subscribe)

  1. 发布/订阅模式:一个生产者,多个消费者,每一个消费者都有自己的一个队列,生产者没有将消息直接发送到队列,而是发送到了交换机,每个队列绑定交换机,生产者发送的消息经过交换机,到达队列,实现一个消息被多个消费者获取的目的。
  2. 如果将消息发送到一个没有队列绑定的exchange上,那么该消息将会丢失,因为在RabbitMQ中Exchange不具备存储消息的能力,只有队列具备存储消息的能力。
  3. RabbitMQ中交换器主要有四种类型:direct fanout topic headers ,发布/订阅模式下交换器类型是fanout。

 

类库

RabbitMQ使用的是AMQP协议。要使用她你就必须需要一个使用同样协议的库。这里使用php-amqplib,并且使用Composer依赖管理。

# 项目中添加一个composer.json文件
{
    "require": {
        "php-amqplib/php-amqplib": "^2.11"
    }
}
# 使用Composer安装(前提要安装Composer)
composer install

生产者(消息发送方)

 生产者连接到 RabbitMQ Broker,建立一个连接(Connection),开启一个信道(Channel)。生产者将消息发送给交换器(Exchange),交换器(Exchange)一边接收生产者消息,一边将消息推送到队列。这里交换器(Exchange)类型时fanout表示推送所有队列。查看生产者代码(publish.php)。

<?php
require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../conf/config.php';

use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Exchange\AMQPExchangeType;

$exchange = 'pubSub'; // 交换器名称

try {
    $connection = new AMQPStreamConnection(HOST, PORT, USER, PASS, VHOST); // 建立连接到RabbitMQ服务器
    $channel = $connection->channel(); // 建立通道
    // 交换器类型定义fanout。
    $channel->exchange_declare($exchange, AMQPExchangeType::FANOUT, false, false, false); // 试探性声明一个交换机

    // 生产10条消息
    for ($i = 0; $i < 10; $i++) {
        $body = "发布/订阅模式下生成第【" . ($i + 1) . "】条消息";
        $message = new AMQPMessage($body, ['content_type' => 'text/plain', 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT]);
        $channel->basic_publish($message, $exchange);
        echo ' [x] Sent ' . $body . "\n";
        sleep(1);
    }
    $channel->close(); // 关闭通道
    $connection->close(); // 关闭连接
} catch (Exception $e) {
    die($e->getMessage());
}

消费者(消息的接收方) 

生产者连接到 RabbitMQ Broker,建立一个连接(Connection),开启一个信道(Channel)。声明一个队列和交换器,将队列绑定到交换器(Exchange),交换器(Exchange)将消息发送到队列中,消费者从队列中消费消息。查看消费者代码(receive.php)。

<?php
require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../conf/config.php';

use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Exchange\AMQPExchangeType;

$exchange = 'pubSub'; // 交换器名称

try {
    $connection = new AMQPStreamConnection(HOST, PORT, USER, PASS, VHOST); // 建立连接到RabbitMQ服务器
    $channel = $connection->channel(); // 建立通道
    $channel->exchange_declare($exchange, AMQPExchangeType::FANOUT, false, false, false); // 试探性声明一个交换机
    // 创建了一个具有生成名称的非持久队 当我们连接上RabbitMQ的时候,我们需要一个全新的、空的队列。我们可以手动创建一个随机的队列名,或者让服务器为我们选择一个随机的队列名(推荐)
    list($queueName, ,) = $channel->queue_declare('', false, false, false, false); // 临时队列
    $channel->queue_bind($queueName, $exchange); // 队列绑定交换器

    echo " [*] Waiting for messages. To exit press CTRL+C\n";
    $callback = function ($msg) { // 回调函数
        sleep(3);
        // 手动确认消息是否正常消费,保证消息消费的幂等。
        $msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
        echo ' [x] Received ', $msg->body, "\n";
    };
    // basic_qos方法设置参数prefetch_count = 1。这告诉RabbitMQ不要在一个时间给一个消费者多个消息(在处理和确认以前的消息之前,不要向消费者发送新消息。相反,它将发送给下一个仍然不忙的消费者)。
    $channel->basic_qos(null, 1, null);
    $channel->basic_consume($queueName, '', false, false, false, false, $callback);
    while ($channel->is_consuming()) { // 循环获取消息
        $channel->wait();
    }
    $channel->close();
    $connection->close();
} catch (Exception $e) {
    die($e->getMessage());
}

 测试

  1. 首先开启两个及以上消费者,之后开启一个生产者,如果先开启生产者,没有队列绑定交换器,这些消息会被忽略。
  2. 查看结果                                                                                                                                                                      

 

其他

  1. 如果对一个概念不了解,查看 RabbitMQ基本概念
  2. 如果对里面一些方法不了解,查看RabbitMQ各方法详解(PHP版)
  3. 代码下载

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值