一、生产者
//SendQuee.php
public function handle()
{
//1.0 建立生产者与mq之间的连接
$conn = new AMQPStreamConnection(
getenv('RB_HOST'),
getenv('RB_PORT'),
getenv('RB_USER'),
getenv('RB_PWD'),
getenv('RB_VHOST')
);
//2.0 已连接基础上建立生产者与mq之间的通道
$channel = $conn->channel();
//3.1 声明初始化交换机
$channel->exchange_declare(getenv('RB_EXCHANGE'), getenv('RB_TYPE'), false, true, false);
//3.2 声明初始化一条队列
$channel->queue_declare(getenv('RB_QUEUE_CZ'), false, true, false, false, false);
//3.3 将队列与交换机进行绑定,并使用路由关键字
$channel->queue_bind(getenv('RB_QUEUE_CZ'), getenv('RB_EXCHANGE'), getenv('RB_ROUTING_KEY'));
//4.0 推送队列
for($i=0; $i<=5; $i++){
$array = ["a$i"=>1, "b$i"=>2, "c$i"=>3];
$data = json_encode($array);
//生成消息
$msg = new AMQPMessage($data);
$channel->basic_publish($msg, getenv('RB_EXCHANGE'), getenv('RB_ROUTING_KEY'));
}
//5.0 关闭
$channel->close();
$conn->close();
}
二、消费者
public function handle()
{
$this->basic_get();
}
public function basic_get()
{
//1.0 建立消费者与mq之间的连接
$conn = new AMQPStreamConnection(
getenv('RB_HOST'),
getenv('RB_PORT'),
getenv('RB_USER'),
getenv('RB_PWD'),
getenv('RB_VHOST')
);
$channel = $conn->channel();
//2.0 已连接基础上建立消费者与mq之间的通道
$channel = $conn->channel();
//3.1 声明初始化交换机
$channel->exchange_declare(getenv('RB_EXCHANGE'), getenv('RB_TYPE'), false, true, false);
//3.2 声明初始化一条队列
$channel->queue_declare(getenv('RB_QUEUE_CZ'), false, true, false, false, false);
//3.3 绑定
$channel->queue_bind(getenv('RB_QUEUE_CZ'), getenv('RB_EXCHANGE'), getenv('RB_ROUTING_KEY'));
//4.0 消费
for($i=1; $i<200; $i++){
/**
* @param string $queue 队列名称
* @param bool $no_ack 是否自动回复:true:自动回复;false:手动回复
**/
$message = $channel->basic_get(getenv('RB_QUEUE_1_CZ'));
if(!isset($message->body)) break;
\Log::info("第 ".$message->delivery_info['delivery_tag']." 个 消息:".$message->body);
//手动 ack 消息
$message->delivery_info['channel']->basic_ack($message->delivery_info['delivery_tag']);
}
//5.0 关闭连接
$channel->close();
$conn->close();
}
public function basic_consume()
{
//1.0 连接 Rabbitmq
$conn = new AMQPStreamConnection(
getenv('RB_HOST'),
getenv('RB_PORT'),
getenv('RB_USER'),
getenv('RB_PWD'),
getenv('RB_VHOST')
);
$channel = $conn->channel();
//2.0 连接channel
$channel = $conn->channel();
//3.1 声明初始化交换机
$channel->exchange_declare(getenv('RB_EXCHANGE'), getenv('RB_TYPE'), false, true, false);
//3.2 声明初始化一条队列
$channel->queue_declare(getenv('RB_QUEUE_CZ'), false, true, false, false, false);
//3.3 绑定
$channel->queue_bind(getenv('RB_QUEUE_CZ'), getenv('RB_EXCHANGE'), getenv('RB_ROUTING_KEY'));
$callback = function($message){
\Log::info("第 ".$message->delivery_info['delivery_tag']." 个 消息:".$message->body);
//手动 ack 消息
$message->delivery_info['channel']->basic_ack($message->delivery_info['delivery_tag']);
};
/**
* @param string $queue 队列名称
* @param string $consumer_tag 消费者标签,用来标识消费者的,在监听队列时设置
* @param bool $no_local
* @param bool $no_ack 是否自动回复:true:自动回复;false:手动回复
* @param bool $exclusive
* @param bool $nowait
* @param callable|null $callback
**/
$channel->basic_consume(getenv('RB_QUEUE_CZ'), '', false, true, false, false, $callback);
while(count($channel->callbacks)) {
$channel->wait();
}
//6.0 关闭连接
$channel->close();
$conn->close();
}
在RabbitMQ中消费者有2种方式获取队列中的消息:
a) 一种是通过basic.consume命令,订阅某一个队列中的消息,channel会自动在处理完上一条消息之后,接收下一条消息。(同一个channel消息处理是串行的)。除非关闭channel或者取消订阅,否则客户端将会一直接收队列的消息。
b) 另外一种方式是通过basic.get命令主动获取队列中的消息,但是绝对不可以通过循环调用basic.get来代替basic.consume,这是因为basic.get在实际执行的时候,是首先consume某一个队列,然后检索第一条消息,然后再取消订阅。如果是高吞吐率的消费者,最好还是建议使用basic.consume。
简单总结一下就是说:
consume是只要队列里面还有消息就一直取。
get是只取了队列里面的第一条消息。
因为get开销大,如果需要从一个队列取消息的话,首选consume方式,慎用循环get方式。
参考资料:
RabbitMQ BasicGet与BasicConsume的区别
php-amqplib库操作RabbitMQ
Springboot 整合RabbitMq ,用心看完这一篇就够了
深入解析RabbitMQ消息应答ack机制
RabbitMq初探——消息确认