PHP 安装和使用RabbitMQ消息队列

安装
$ composer require php-amqplib/php-amqplib
可视化管理
  • 在RabbitMQ命令工具中开启web management
$ rabbitmq-plugins enable rabbitmq_management
  • 在浏览器输入网址[host]:15672进入登录页面,[host]为部署RabbitMQ的主机地址
    登录界面
  • 输入登录账户密码(初始都是guest)后进入管理页面
    在这里插入图片描述
Connect
<?php

require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();

// do something ...

$channel->close();
$connection->close();
Tips
  • 如果在正常连接时抛出AMQPTimeoutExceptionAMQPHeartbeatMissedException或一些其他异常可能是版本问题,可以使用更高版本的PHP或更换其他版本的php-amqplib
Hello World
  • 概述
    在这里插入图片描述
    一个简单的一对一消息队列示例,basic_consume的第四个参数$no_ack设为true时,消息在接收后会自动应答
  • send.php
<?php

$channel->queue_declare('hello', false, false, false, false);

$msg = new AMQPMessage('Hello World!');
$channel->basic_publish($msg, '', 'hello');

echo " [x] Sent 'Hello World!'\n";
<?php

$channel->queue_declare('hello', false, false, false, false);

echo " [*] Waiting for messages. To exit press CTRL+C\n";

$callback = function ($msg) {
    echo ' [x] Received ', $msg->body, "\n";
};

$channel->basic_consume('hello', '', false, true, false, false, $callback);

while ($channel->is_open()) {
    $channel->wait();
}
  • 开启一个终端执行receive.php接收消息
$ php receive.php
 [*] Waiting for messages. To exit press CTRL+C
 [x] Received Hello World!
 [x] Received Hello World!
 [x] Received Hello World!
  • 开启一个终端执行send.php发送消息
$ php send.php
 [x] Sent 'Hello World!'
$ php send.php
 [x] Sent 'Hello World!'
$ php send.php
 [x] Sent 'Hello World!'
Work Queues
  • 概述
    一对多消息队列示例,basic_consume的第四个参数$no_ackfalse时,消息在接收后需要手动应答,在已接收未应答时消息状态会从ready变为unacked,此时其他消费者不能接收此消息,如果该消费者客户端在关闭连接后依然没有应答,该消息状态会由unacked转为ready可以重新被消费
    在这里插入图片描述
  • worker.php
<?php

$channel->queue_declare('task_queue', false, true, false, false);

echo " [*] Waiting for messages. To exit press CTRL+C\n";

$callback = function ($msg) {
    echo ' [x] Received ', $msg->body, "\n";
    sleep(substr_count($msg->body, '.'));
    $msg->ack();
};

$channel->basic_qos(null, 1, null);
$channel->basic_consume('task_queue', '', false, false, false, false, $callback);

while ($channel->is_open()) {
    $channel->wait();
}
<?php

$channel->queue_declare('task_queue', false, true, false, false);

$data = implode(' ', array_slice($argv, 1));
if (empty($data)) {
    $data = "Hello World!";
}
$msg = new AMQPMessage(
    $data,
    array('delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT)
);

$channel->basic_publish($msg, '', 'task_queue');

echo ' [x] Sent ', $data, "\n";
  • 开启两个终端执行worker.php,然后再开启一个终端执行new_task.php
php new_task.php First message.
php new_task.php Second message..
php new_task.php Third message...
php new_task.php Fourth message....
php new_task.php Fifth message.....
  • worker1
php worker.php
 [*] Waiting for messages. To exit press CTRL+C
 [x] Received First message.
 [x] Received Third message...
 [x] Received Fifth message.....
  • worker2
php worker.php
 [*] Waiting for messages. To exit press CTRL+C
 [x] Received Second message..
 [x] Received Fourth message....
Publish/Subscribe
  • 概述
    发布订阅的消息模式示例,这一部分会用到exchange来做消息投递,而在之前的消息示例中没有声明exchange时都是使用的默认exchange来进行指定队列的消息投递,这里会使用一个fanout类型的exchange来进行消息的广播式投递,此时发布到这个exchange的消息会投递到所有绑定的队列中
    在这里插入图片描述
  • emit_log.php
<?php

$channel->exchange_declare('logs', 'fanout', false, false, false);

$data = implode(' ', array_slice($argv, 1));
if (empty($data)) {
    $data = "info: Hello World!";
}
$msg = new AMQPMessage($data);

$channel->basic_publish($msg, 'logs');

echo ' [x] Sent ', $data, "\n";
<?php

$channel->exchange_declare('logs', 'fanout', false, false, false);

list($queue_name, ,) = $channel->queue_declare("", false, false, true, false);

$channel->queue_bind($queue_name, 'logs');

echo " [*] Waiting for logs. To exit press CTRL+C\n";

$callback = function ($msg) {
    echo ' [x] ', $msg->body, "\n";
};

$channel->basic_consume($queue_name, '', false, true, false, false, $callback);

while ($channel->is_open()) {
    $channel->wait();
}
  • 开启两个终端执行receive_logs.php,然后执行emit_log.php
php emit_log.php
 [x] Sent info: Hello World!
  • consumer1
php receive_logs.php
 [*] Waiting for logs. To exit press CTRL+C
 [x] info: Hello World!
  • consumer2
php receive_logs.php
 [*] Waiting for logs. To exit press CTRL+C
 [x] info: Hello World!
Routing
  • 概述
    路由的消息模式示例,这里会使用一个direct类型的exchange来处理消息,在接收消息时将队列绑定到exchangerounting_key,发送消息时再指定exchangerounting_key,这样消息就只会投递到指定交换机和路由下的队列中
    在这里插入图片描述
  • emit_log_direct.php
$channel->exchange_declare('direct_logs', 'direct', false, false, false);

$severity = isset($argv[1]) && !empty($argv[1]) ? $argv[1] : 'info';

$data = implode(' ', array_slice($argv, 2));
if (empty($data)) {
    $data = "Hello World!";
}

$msg = new AMQPMessage($data);

$channel->basic_publish($msg, 'direct_logs', $severity);

echo ' [x] Sent ', $severity, ':', $data, "\n";
$channel->exchange_declare('direct_logs', 'direct', false, false, false);

list($queue_name, ,) = $channel->queue_declare("", false, false, true, false);

$severities = array_slice($argv, 1);
if (empty($severities)) {
    file_put_contents('php://stderr', "Usage: $argv[0] [info] [warning] [error]\n");
    exit(1);
}

foreach ($severities as $severity) {
    $channel->queue_bind($queue_name, 'direct_logs', $severity);
}

echo " [*] Waiting for logs. To exit press CTRL+C\n";

$callback = function ($msg) {
    echo ' [x] ', $msg->delivery_info['routing_key'], ':', $msg->body, "\n";
};

$channel->basic_consume($queue_name, '', false, true, false, false, $callback);

while ($channel->is_open()) {
    $channel->wait();
}
  • 接收端,只接收指定路由的消息
php receive_logs_direct.php warning error
 [*] Waiting for logs. To exit press CTRL+C
 [x] warning:this is a warning
 [x] error:this is an error
  • 发送端
php emit_log_direct.php info "this is an info"
php emit_log_direct.php warning "this is a warning"
php emit_log_direct.php error "this is an error"
 [x] Sent info:this is an info
 [x] Sent warning:this is a warning
 [x] Sent error:this is an error 
Topic
  • 概述
    更灵活的路由消息模式示例,可以根据类别匹配路由,这里会使用一个topic类型的exchange来处理消息,消息的收发流程和direct类似,关键在于rounting_key的匹配规则:
    1. rounting_key可以由多个单词组成(以.分隔),总长度不超过255字节
    2. *可以匹配一个任意的单词(例a.*可以匹配a.ba.c但不能匹配a.b.c)
    3. #可以匹配零个或多个任意的单词(例a.#可以匹配a.ba.b.c)
      在这里插入图片描述
  • emit_log_topic.php
  • receive_logs_topic.php
// 代码和上小节的direct类型类似,只需将交换机的类型声明为topic
$channel->exchange_declare('topic_logs', 'topic', false, false, false);
  • 执行receive_logs_topic.php,以下任意一个rounting_key都可接收到kern.critical路由的消息
php receive_logs_topic.php "#" "kern.*" "*.critical" 
 [*] Waiting for logs. To exit press CTRL+C
 [x] kern.critical:A critical kernel error
  • 执行emit_log_topic.php发送消息
php emit_log_topic.php "kern.critical" "A critical kernel error"
 [x] Sent kern.critical:A critical kernel error
RPC
  • 概述
    远程调用模式,可以实现服务间的调用和通信,大致的流程为:
    1. 客户端创建并消费一个临时回调队列,然后用消息内容、生成的唯一ID和回调队列名创建消息对象发送至服务端队列
    2. 服务端从队列取出消息并处理完成后,将消息中的唯一ID和需要响应的数据创建消息对象投递回消息中的回调队列
    3. 客户端创建的回调队列消费到服务端响应的消息,判断消息中的唯一ID和发送时的一致即为正确响应的消息
      在这里插入图片描述
  • 执行rpc_server.php,等待计算并返回斐波那契数的值
php rpc_server.php
 [x] Awaiting RPC requests
 [.] fib(30)
php rpc_client.php
 [.] Got 832040
Publisher Confirms
  • 概述
    用于对发布的消息进行确认,以确保消息能正确发布,避免丢失;开启发布确认机制后,在执行消息发送操作后,会有一个确认发布和一个未确认发布的异步回调处理方法来处理此次发布结果,在设定的超时时间内未发布出去的消息会进入未确认发布的异步回调方法中,然后可以对未发布的消息进行后续处理,例如重发等
// 开启发布确认
$channel->confirm_select();

$channel->queue_declare('hello', false, true, false, false);
$msg = new AMQPMessage(
    'Hello World!',
    array('delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT)
);
$channel->basic_publish($msg, '', 'hello');

// 已确认发布时的回调方法
$channel->set_ack_handler(
    function (AMQPMessage $message){
        // code when message is confirmed
        echo ' [x] Sent ', $message->body, "\n";
    }
);
// 未确认发布时的回调方法
$channel->set_nack_handler(
    function (AMQPMessage $message){
        // code when message is nack-ed
        echo ' [x] Sent ', $message->body, " failed\n";
    }
);
// 设置超时时间
$channel->wait_for_pending_acks(5);
Website

RabbitMQ

RabbitMQ for GitHub

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值