thinkPHP6使用RabbitMQ实现日志处理
- 1.安装RabbitMQ依赖
- 2.在config目录下新建rabbitmq.php,保存RabbitMQ配置信息
- 3.创建RabbitMQ服务接口,在app/service目录下创建RabbitMQServiceInterface.php文件
- 4.实现RabbitMQ服务的接口,在app/service目录下创建RabbitMQService.php实现接口中定义的方法
- 4.创建RabbitMQServiceFactory,在app/service下创建RabbitMQServiceProvider.php
- 5.在app/provider目录下创建RabbitMQServiceProvider.php向外提供服务
- 6.在app/service.php中添加服务
- 7.创建测试日志RabbitMQ的生产者逻辑,在app/api/controller下创建Logger.php
- 8.创建日志系统RabbitMQ的消费者,用command命令创建LoggerCommer
- 9.编辑app/command/LoggerComer.php
- 10. 在app/consumer目录下创建LoggerConsumer.php
- 11.在app/service目录下创建LoggerService.php处理日志信息入库操作
- 12.运行测试接口,发送日志数据到RabbitMQ并消费
- 终:附上测试数据表结构
1.安装RabbitMQ依赖
composer require php-amqplib/php-amqplib
你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。
2.在config目录下新建rabbitmq.php,保存RabbitMQ配置信息
<?php
<?php
// 示例配置文件
return [
# 连接信息
'AMQP' => [
'host' => '127.0.0.1', //连接rabbitmq,此为安装rabbitmq服务器
'port'=>'5672',
'user'=>'guest',
'password'=>'pwd',
'vhost'=>'/'
],
# 邮件队列
'direct_queue' => [
'exchange_name' => 'direct_exchange',
'exchange_type'=>'direct',#直连模式
'queue_name' => 'direct_queue',
'route_key' => 'direct_roteking',
'consumer_tag' => 'direct'
],
'fanout_queue' => [
'exchange_name' => 'fanout_exchange',
'exchange_type'=>'fanout',#直连模式
'queue_name' => 'fanout_queue',
'route_key' => 'fanout_roteking',
'consumer_tag' => 'fanout'
],
'topic_queue' => [
'exchange_name' => 'topic_exchange',
'exchange_type'=>'topic',#直连模式
'queue_name' => 'topic_queue',
'route_key' => 'topic_roteking',
'consumer_tag' => 'topic'
],
'headers_queue' => [
'exchange_name' => 'headers_exchange',
'exchange_type'=>'headers',#直连模式
'queue_name' => 'headers_queue',
'route_key' => 'headers_roteking',
'consumer_tag' => 'headers'
],
];
3.创建RabbitMQ服务接口,在app/service目录下创建RabbitMQServiceInterface.php文件
<?php
namespace app\service;
interface RabbitMQServiceInterface
{
public function publish(string $exchange, string $type, string $routingKey, string $queueName, array $message);
public function consume(string $exchange, string $type, string $routingKey,string $queueName, callable $callback);
}
4.实现RabbitMQ服务的接口,在app/service目录下创建RabbitMQService.php实现接口中定义的方法
<?php
declare (strict_types = 1);
namespace app\service;
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use think\Exception;
class RabbitMQService implements RabbitMQServiceInterface
{
private $connection;
private $channel;
public function __construct(string $host, int $port, string $user, string $password, string $vhost = '/')
{
$this->connection = new AMQPStreamConnection($host, $port, $user, $password, $vhost);
$this->channel = $this->connection->channel();
}
public function __destruct()
{
$this->channel->close();
$this->connection->close();
}
/**
* @deprecated 创建交换价和绑定队列
* @param $exchange
* @param $type
* @param $routingKey
* @param $queueName
* @return void
*/
public function createExchangeAndQueue($exchange, $type, $routingKey, $queueName)
{
/*
* 创建交换机(Exchange)
* name: vckai_exchange// 交换机名称
* type: direct // 交换机类型,分别为direct/fanout/topic,参考另外文章的Exchange Type说明。
* passive: false // 如果设置true存在则返回OK,否则就报错。设置false存在返回OK,不存在则自动创建
* durable: false // 是否持久化,设置false是存放到内存中的,RabbitMQ重启后会丢失
* auto_delete: false // 是否自动删除,当最后一个消费者断开连接之后队列是否自动被删除
*/
$this->channel->exchange_declare($exchange, $type, false, false, false);
/*
* 绑定队列和交换机
* @param string $queue 队列名称
* @param string $exchange 交换器名称
* @param string $routing_key 路由key
* @param bool $nowait
* @param array $arguments
* @param int|null $ticket
* @throws \PhpAmqpLib\Exception\AMQPTimeoutException if the specified operation timeout was exceeded
* @return mixed|null
*/
$this->channel->queue_bind($queueName, $exchange, $routingKey);
}
/**
* @deprecated 发布数据
* @param string $exchange
* @param string $type
* @param string $routingKey
* @param string $queueName
* @param array $message
* @return void
*/
public function publish(string $exchange, string $type,string $routingKey, string $queueName, array $message)
{
if (!in_array($type, ['direct', 'fanout', 'topic', 'headers'])){
throw new Exception('交换机类型错误');
}
$this->createExchangeAndQueue($exchange, $type, $routingKey, $queueName);
/*
* 创建AMQP消息类型
* $messageBody:消息体
* delivery_mode 消息是否持久化
* AMQPMessage::DELIVERY_MODE_NON_PERSISTENT = 1; 不持久化
* AMQPMessage::DELIVERY_MODE_PERSISTENT = 2; 持久化
*/
$msg = new AMQPMessage(json_encode($message), array('content_type' => 'text/plain', 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT));
/*
* 发送消息
* msg // AMQP消息内容
* exchange // 交换机名称
* routing key // 路由键名称
*/
$this->channel->basic_publish($msg, $exchange, $routingKey);
}
/**
* @deprecated 消费数据
* @param string $exchange
* @param string $type
* @param string $routingKey
* @param string $queueName
* @param callable $callback
* @return void
*/
public function consume(string $exchange, string $type, string $routingKey,string $queueName, callable $callback)
{
$this->createExchangeAndQueue($exchange, $type, $routingKey, $queueName);
$this->channel->basic_consume($queueName, '', false, true, false, false, $callback);
while ($this->channel->is_consuming()) {
$this->channel->wait();
}
}
}
4.创建RabbitMQServiceFactory,在app/service下创建RabbitMQServiceProvider.php
<?php
namespace app\service;
class RabbitMQServiceFactory
{
public static function create(): RabbitMQServiceInterface
{
$config = config('rabbitmq.AMQP');
return new RabbitMQService(
$config['host'],
$config['port'],
$config['user'],
$config['password'],
$config['vhost']
);
}
}
5.在app/provider目录下创建RabbitMQServiceProvider.php向外提供服务
<?php
namespace app\provider;
use think\facade\App;
use app\service\RabbitMQServiceFactory;
use app\service\RabbitMQServiceInterface;
use \think\Service;
class RabbitMQServiceProvider extends Service
{
public function register()
{
App::bind(RabbitMQServiceInterface::class, function () {
return RabbitMQServiceFactory::create();
});
}
}
6.在app/service.php中添加服务
<?php
use app\AppService;
// 系统服务定义文件
// 服务在完成全局初始化之后执行
return [
AppService::class,
\app\provider\RabbitMQServiceProvider::class,//RabbitMQ服务
];
7.创建测试日志RabbitMQ的生产者逻辑,在app/api/controller下创建Logger.php
<?php
namespace app\api\controller;
use app\service\RabbitMQServiceInterface;
class Logger {
private $amqpDetails;
public function __construct() {
$this->amqpDetails = config('rabbitmq.fanout_queue');
}
public function log() {
$rabbitMQService = app(RabbitMQServiceInterface::class);
$message = [
'message'=>'日志测试数据',
'level'=>'L',
'context'=>'数据内容,记录时间:' . date('Y-m-d H:i:s'),
];
$rabbitMQService->publish('logs', 'fanout', $this->amqpDetails['route_key'], $this->amqpDetails['queue_name'], $message);
echo 'ok';
}
}
8.创建日志系统RabbitMQ的消费者,用command命令创建LoggerCommer
php think make:command LoggerComer LoggerCommer
9.编辑app/command/LoggerComer.php
<?php
declare (strict_types = 1);
namespace app\command;
use app\consumer\LoggerConsumer;
use think\console\Command;
use think\console\Input;
use think\console\Output;
class LoggerComer extends Command
{
protected function configure()
{
// 指令配置
$this->setName('loggercomer')
->setDescription('the loggercomer command');
}
protected function execute(Input $input, Output $output)
{
$consumer = new LoggerConsumer();
$consumer->run();//日志消费者处理逻辑
}
}
10. 在app/consumer目录下创建LoggerConsumer.php
<?php
namespace app\consumer;
use app\service\LoggerService;
use app\service\RabbitMQServiceInterface;
use PhpAmqpLib\Message\AMQPMessage;
class LoggerConsumer
{
public function __construct()
{
$this->amqpDetails = config('rabbitmq.fanout_queue');
}
public function processMessage(AMQPMessage $msg)
{
if($msg->body == 'failed'){
}else{
$service = new LoggerService();//日志处理服务
$message = json_decode($msg->body, true);
$service->log($message);//记录日志到数据库中
echo 'Log message received: ', $msg->body, "\n";
}
}
public function run()
{
$rabbitMQService = app(RabbitMQServiceInterface::class);
$callback = array($this, 'processMessage');
$rabbitMQService->consume('logs', 'fanout', $this->amqpDetails['route_key'], $this->amqpDetails['queue_name'], $callback);
// Wait for messages
while (count($this->channel->callbacks)) {
$this->channel->wait();
}
}
}
11.在app/service目录下创建LoggerService.php处理日志信息入库操作
<?php
declare (strict_types = 1);
namespace app\service;
use app\model\Logs;
use app\validate\LoggerValidate;
class LoggerService
{
protected $logsModel;
protected $LoggerValidate;
public function __construct()
{
$this->logsModel = new Logs();
$this->LoggerValidate = new LoggerValidate();
}
public function log($data)
{
if(!$this->LoggerValidate->check($data)){//数据验证,可根据实际情况去掉或修改
return false;
}
$this->logsModel->save([
'message'=> $data['message'],
'level'=> $data['level'],
'context'=> $data['context'],
]);
}
}
12.运行测试接口,发送日志数据到RabbitMQ并消费
a.中断运行消费者命令
b.触发接口,发送数据
c.终端输出数据及数据库数据
终:附上测试数据表结构
CREATE TABLE `tp_logs` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`message` varchar(255) NOT NULL,
`level` varchar(255) NOT NULL,
`context` text NOT NULL,
`delete_time` timestamp NULL DEFAULT NULL,
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;