1 先composer安装类库
composer requirer php-amqplib/php-amqplib
2 新建配置文件 config/rabbitmq.php
return [
# 连接信息
'AMQP' => [
'host' => '127.0.0.1',
'port'=>'5672',
'login'=>'guest',
'password'=>'guest',
'vhost'=>'/'
],
# 邮件队列
'email_queue' => [
'exchange_name' => 'email_exchange',
'exchange_type'=>'direct',#直连模式
'queue_name' => 'email_queue',
'route_key' => 'email_roteking',
'consumer_tag' => 'consumer'
]
];
3 新建两个类,一个生产者,一个消费者,生产者把消息推送到队列,推送完可以断开连接,消费者要保持一直开启状态消费者保持开启状态的方式1 linux上写定时任务每隔5分钟运行下该脚本,保证访问服务器的ip比较平缓,不至于崩溃2 nohup php index.php index/Message_Consume/start & 用nohup命令后台运行该脚本3 使用Supervisor.以上都是在linux 上的后台执行的方法,在windows上我们只能用命令行做了,当然windowns可以用任务计划做,有兴趣的同学自己去研究下.我们现在把生产者写成接口的形式,消费者一般都写成命令行的方式或者脚本的形式,因为消费者要保持开启的状态
生产者
namespace app;
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use think\facade\Log;
class MqProducer
{
public static function pushMessage($data)
{
$param = config('rabbitmq.AMQP');
$amqpDetail = config('rabbitmq.email_queue');
$connection = new AMQPStreamConnection(
$param['host'],
$param['port'],
$param['login'],
$param['password'],
$param['vhost']
);
$channel = $connection->channel();
/*
name: $queue 创建队列
passive: false
持久durable: true // //队列将在服务器重启后继续存在
互斥exclusive: false // 队列可以通过其他渠道访问
auto_delete: false 通道关闭后,队列不会被删除
*/
$channel->queue_declare($amqpDetail['queue_name'], false, true, false, false);
/*
name: $exchange 创建交换机
type: direct 直连方式
passive: false
durable: true 持久// 交换器将在服务器重启后继续存在
auto_delete: false //一旦通道关闭,交换器将不会被删除。
*/
$channel->exchange_declare($amqpDetail['exchange_name'], 'direct', false, true, false);
/*
$messageBody:消息体
content_type:消息的类型 可以不指定
delivery_mode:消息持久化最关键的参数
AMQPMessage::DELIVERY_MODE_NON_PERSISTENT = 1;
AMQPMessage::DELIVERY_MODE_PERSISTENT = 2;
*/
$messageBody = $data;
$message = new AMQPMessage($messageBody, array('content_type' => 'text/plain', 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT));
$channel->basic_publish($message, $amqpDetail['exchange_name'],$amqpDetail['route_key']);
$channel->close();
$connection->close();
echo "ok";
}
}
消费者代码
namespace app;
use PhpAmqpLib\Connection\AMQPStreamConnection;
use think\facade\Log;
class MqConsumer
{
/**
* 消费端 消费端需要保持运行状态实现方式
* 1 linux上写定时任务每隔5分钟运行下该脚本,保证访问服务器的ip比较平缓,不至于崩溃
* 2 nohup php index.php index/Message_Consume/start & 用nohup命令后台运行该脚本
* 3
**/
function shutdown($channel, $connection)
{
$channel->close();
$connection->close();
Log::write("closed",3);
}
function process_message($message)
{
echo $message->body."\n";
//手动发送ack
$message->delivery_info['channel']->basic_ack($message->delivery_info['delivery_tag']);
// Send a message with the string "quit" to cancel the consumer.
if ($message->body === 'quit') {
$message->delivery_info['channel']->basic_cancel($message->delivery_info['consumer_tag']);
}
}
/**
* 启动
* @return \think\Response
*/
public function start()
{
$param = config('rabbitmq.AMQP');
$amqpDetail = config('rabbitmq.email_queue');
$connection = new AMQPStreamConnection(
$param['host'],
$param['port'],
$param['login'],
$param['password'],
$param['vhost']
);
$channel = $connection->channel();
$channel->queue_declare($amqpDetail['queue_name'], false, true, false, false);
$channel->exchange_declare($amqpDetail['exchange_name'], 'direct', false, true, false);
$channel->queue_bind($amqpDetail['queue_name'], $amqpDetail['exchange_name'],$amqpDetail['route_key']);
/*
queue: 从哪里获取消息的队列
consumer_tag: 消费者标识符
no_local: 不接收此使用者发布的消息
no_ack: 如果求设置为true,则此使用者将使用自动确认模式。详情请参见.
exclusive:请独占使用者访问,这意味着只有这个使用者可以访问队列
nowait:
callback: :PHP回调 array($this, 'process_message') 调用本对象的process_message方法
*/
$channel->basic_consume($amqpDetail['queue_name'], $amqpDetail['consumer_tag'], false, false, false, false, array($this, 'process_message'));
register_shutdown_function(array($this, 'shutdown'), $channel, $connection);
while (count($channel->callbacks)) {
$channel->wait();
}
}
}
4 以上是生产者和消费者的两个类,下面我们把我们先自定义一个命令行,然后用这个命令行执行我们的消费者去消费消息
php think make:command Consumer
生成命令行,稍作修改
declare (strict_types = 1);
namespace app\command;
use app\MqConsumer;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
class Consumer extends Command
{
protected function configure()
{
// 指令配置
$this->setName('consumer')//自定义的命令 php think consumer 这样去调用了
->setDescription('the consumer command');
}
protected function execute(Input $input, Output $output)
{
$consumer = new MqConsumer();
$consumer->start();//调用消费者
}
当然tp框架自定义命令行还需要把该命令添加到config/console.php中
添加命令到配置文件config/console.php
return [
// 指令定义
'commands' => [
// consumer是app\Command\Consumer文件中自定义命令行的名字
'consumer' => 'app\Command\Consumer'
],
];
添加完后测试php think consumer 命令是否有效
5 消费者写好后,我们控制器写一个接口,模拟用户访问
namespace app\controller;
use app\BaseController;
use app\MqProducer;
class Index extends BaseController
{
public function send()
{
$consumer = new MqProducer();//生产者
$consumer->pushMessage('time:'.time().' your mother call you back home ');
}
}
6 当然我们也可以用ab压力测试模拟下并发访问,但是先开启消费端 才能看到效果
ab -n 100 -c 10 127.0.0.1/think/public/index.php/send
-n 访问次数
-c 并发量