参考资料
https://blog.csdn.net/demon3182/article/details/77482725
延时队列参考资料
https://blog.csdn.net/u012119576/article/details/74677835
如果你希望使用一个可靠性高、功能强大、易于管理的消息队列系统那么就选择RabbitMQ吧,如果你想用一个性能高,但偶尔丢点数据不是很在乎可以使用kafka或者zeroMQ
下面封装了一个类,实现了一个添加队列和一个消费队列:
<?php
/**
* @note: 消息队列类
* @author: wuLibo
* createTime: 2018/8/10 14:32
*/
//引用所需文件
require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Wire\AMQPTable;
/**
* Class RabbitMQ
*/
class RabbitMQ {
private $connection; //连接
private $channel; //渠道
private $logFileUrl = './log/';
public function __construct($config)
{
try{
$this->connection = new AMQPStreamConnection($config['host'], $config['port'], $config['user'], $config['password']);
if(!$this->connection){
throw new \Exception('连接失败');
}
$this->channel = $this->connection->channel();
}catch (\Exception $e){
$this->writeLog("file:{$e->getFile()};line:{$e->getLine()};message:{$e->getMessage()}");
}
}
/**
* @note:添加队列
* @author: wulibo <13488215611@163.com>
* @createTime: 2018/8/10 14:22
* @param $data 数据
* @param $callbackString 命名空间加函数名称用'::'隔开 命名空间必须和路由一致
* @param $name 名称
* @param $expiration 延迟时间 单位秒
* @param string $prefix 前缀
*/
public function pushMessage($data,$callbackString,$name,$expiration,$prefix = 'cache_'){
try{
$cache_exchange_name = $prefix.$name.'_'.$expiration;
$cache_queue_name = $prefix.$name.'_'.$expiration;
$this->channel->exchange_declare('delay_exchange', 'direct',false,false,false);
$this->channel->exchange_declare($cache_exchange_name, 'direct',false,false,false);
$tale = new AMQPTable();
$tale->set('x-dead-letter-exchange', 'delay_exchange');
$tale->set('x-dead-letter-routing-key','delay_exchange');
$tale->set('x-message-ttl',$expiration*1000);
$this->channel->queue_declare($cache_queue_name,false,true,false,false,false,$tale);
$this->channel->queue_bind($cache_queue_name, $cache_exchange_name,'');
$this->channel->queue_declare('delay_queue',false,true,false,false,false);
$this->channel->queue_bind('delay_queue', 'delay_exchange','delay_exchange');
$data['callback'] = $callbackString;
$arr = http_build_query($data);
$msg = new AMQPMessage($arr,array(
'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT,
));
$this->channel->basic_publish($msg,$cache_exchange_name,'');
echo date('Y-m-d H:i:s')." [x] Sent $arr || $expiration ".PHP_EOL;
}catch (\Exception $e){
echo "error(file:{$e->getFile()};line:{$e->getLine()};message:{$e->getMessage()})";
$this->writeLog("file:{$e->getFile()};line:{$e->getLine()};message:{$e->getMessage()}");
}
}
/**
* @note:消费队列
* @author: wulibo <13488215611@163.com>
* @createTime: 2018/8/10 14:22
*/
public function outMessage(){
try{
$this->channel->exchange_declare('delay_exchange', 'direct',false,false,false);
$this->channel->exchange_declare('cache_exchange', 'direct',false,false,false);
$this->channel->queue_declare('delay_queue',false,true,false,false,false);
$this->channel->queue_bind('delay_queue', 'delay_exchange','delay_exchange');
echo ' [*] Waiting for message. To exit press CTRL+C '.PHP_EOL;
$callback = function ($msg){
try{
parse_str($msg->body,$data);
$call = explode('::',$data['callback']);
if(!is_array($call)){
throw new \Exception('参数错误');
}
$file = str_replace('\\','/',$call[0]).'.php';
if(!file_exists($file)){
throw new \Exception('文件加载错误');
}
include_once $file;
$class = new $call[0];
$function = $call[1];
unset($data['callback']);
$class->$function($data);
echo date('Y-m-d H:i:s')." [x] Received",$msg->body,PHP_EOL;
$msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
}catch (\Exception $e){
$this->writeLog("file:{$e->getFile()};line:{$e->getLine()};message:{$e->getMessage()}");
}
};
//只有consumer已经处理并确认了上一条message时queue才分派新的message给它
$this->channel->basic_qos(null, 1, null);
$this->channel->basic_consume('delay_queue','',false,false,false,false,$callback);
while (count($this->channel->callbacks)) {
$this->channel->wait();
}
}catch (\Exception $e){
echo "error(file:{$e->getFile()};line:{$e->getLine()};message:{$e->getMessage()})";
$this->writeLog("file:{$e->getFile()};line:{$e->getLine()};message:{$e->getMessage()}");
}
}
/**
* @note:记录日志
* @author: wulibo <13488215611@163.com>
* @createTime: 2018/8/10 16:39
* @param $data
*/
public function writeLog($data){
$years = date('Y-m');
//设置路径目录信息
$url = $this->logFileUrl.$years.'/'.date('Ymd').'_request_log.txt';
$dir_name=dirname($url);
//目录不存在就创建
if(!file_exists($dir_name))
{
//iconv防止中文名乱码
$res = mkdir(iconv("UTF-8", "GBK", $dir_name),0777,true);
}
$fp = fopen($url,"a");//打开文件资源通道 不存在则自动创建
fwrite($fp,var_export($data,true)."\r\n");//写入文件
fclose($fp);//关闭资源通道
}
public function __destruct()
{
$this->channel->close();
$this->connection->close();
}
}
下面是添加队列的代码:
require __DIR__.'/RabbitMQ.php';
$config = [ 'host'=>'127.0.0.1', 'port'=>5672, 'user'=>'guest', 'password'=>'guest' ];
$RabbitMQ = new RabbitMQ($config); $data['abc'] = 'abc';
$RabbitMQ->pushMessage($data,'test\\test::ccc','demo',1);die;
下面是消费队列代码:
require __DIR__.'/RabbitMQ.php';
$config = [ 'host'=>'127.0.0.1', 'port'=>5672, 'user'=>'guest', 'password'=>'guest' ];
$RabbitMQ = new RabbitMQ($config);
$RabbitMQ->outMessage();die;
亲测通过,大神指教