thinkPHP6使用RabbitMQ实现日志处理

该文详细介绍了如何在ThinkPHP6框架中集成RabbitMQ来处理日志。从安装RabbitMQ依赖开始,接着配置RabbitMQ,创建服务接口和服务实现,编写生产者(日志发送)和消费者(日志入库)逻辑,最后进行测试并展示了日志数据的表结构。
摘要由CSDN通过智能技术生成

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;
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值