thinkPHP6使用RabbitMQ实现日志处理

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并消费
      • a.中断运行消费者命令
      • b.触发接口,发送数据
      • c.终端输出数据及数据库数据
    • 终:附上测试数据表结构

1.安装RabbitMQ依赖

composer require php-amqplib/php-amqplib

你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

2.在config目录下新建rabbitmq.php,保存RabbitMQ配置信息


<?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文件



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实现接口中定义的方法


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



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向外提供服务



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中添加服务



use app\AppService;

// 系统服务定义文件
// 服务在完成全局初始化之后执行
return [
    AppService::class,
    \app\provider\RabbitMQServiceProvider::class,//RabbitMQ服务
];

7.创建测试日志RabbitMQ的生产者逻辑,在app/api/controller下创建Logger.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


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



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处理日志信息入库操作


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.中断运行消费者命令

thinkPHP6使用RabbitMQ实现日志处理_第1张图片

b.触发接口,发送数据

thinkPHP6使用RabbitMQ实现日志处理_第2张图片

c.终端输出数据及数据库数据

thinkPHP6使用RabbitMQ实现日志处理_第3张图片
thinkPHP6使用RabbitMQ实现日志处理_第4张图片

终:附上测试数据表结构

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;

你可能感兴趣的:(Tp6,RabbitMQ,php,rabbitmq)