tp8使用rabbitMQ消息队列的示例

环境:宝塔,debian 13\thinkphp8.1\php8.3

rabbitMQ版本:4.3.2;php-amqplib版本:3.7.4

第一步:composer 必须是官方源,阿里云镜像滞后,安装不了3.7.4这个版本;如何恢复官方源:

bash 复制代码
composer config -g repo.packagist composer https://repo.packagist.org

第二步:install php-amqplib版本:3.7.4;

bash 复制代码
composer require php-amqplib/php-amqplib

第三步:我在tp8创建了多应用模式,创建了api应用;在config/rabbitmq.php;

php 复制代码
<?php

declare(strict_types=1);

return [
    // RabbitMQ 服务地址
    'host' => '127.0.0.1',

    // AMQP 默认端口
    'port' => 5672,

    // 用户名
    'user' => 'admin',

    // 密码
    'password' => 'admin',

    // 虚拟主机
    'vhost' => '/',

    // 队列名称
    'queue' => 'tp8_demo',

    // 连接超时
    'connection_timeout' => 3.0,

    // 读写超时
    // 消费者是长连接,不能设置得过短
    'read_write_timeout' => 60.0,

    // 心跳间隔
    'heartbeat' => 30,
];

第四步:创建发送消息的服务,及在控制器里发送消息示例:app/api/service/RabbitMqService;

php 复制代码
<?php

declare(strict_types=1);

namespace app\api\service;

use PhpAmqpLib\Channel\AMQPChannel;
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use RuntimeException;
use Throwable;

final class RabbitMqService
{
    private ?AMQPStreamConnection $connection = null;

    private ?AMQPChannel $channel = null;

    private string $queue;

    public function __construct()
    {
        $this->queue = (string) config('rabbitmq.queue', 'tp8_demo');
    }

    /**
     * 建立 RabbitMQ 连接。
     */
    public function connect(): void
    {
        if ($this->connection !== null && $this->connection->isConnected()) {
            return;
        }

        $this->connection = new AMQPStreamConnection(
            (string) config('rabbitmq.host', '127.0.0.1'),
            (int) config('rabbitmq.port', 5672),
            (string) config('rabbitmq.user', 'admin'),
            (string) config('rabbitmq.password', 'admin'),
            (string) config('rabbitmq.vhost', '/'),
            false,
            'AMQPLAIN',
            null,
            'en_US',
            (float) config('rabbitmq.connection_timeout', 3.0),
            (float) config('rabbitmq.read_write_timeout', 60.0),
            null,
            false,
            (int) config('rabbitmq.heartbeat', 30)
        );

        $this->channel = $this->connection->channel();

        /*
         * 声明队列。
         *
         * 参数依次是:
         * 1. queue       队列名称
         * 2. passive     是否只检查队列,不创建
         * 3. durable     RabbitMQ 重启后队列是否保留
         * 4. exclusive   是否仅当前连接使用
         * 5. auto_delete 最后一个消费者断开后是否删除
         */
        $this->channel->queue_declare(
            $this->queue,
            false,
            true,
            false,
            false
        );
    }

    /**
     * 发送消息。
     *
     * @param array<string, mixed> $data
     */
    public function publish(array $data): string
    {
        $this->connect();

        if ($this->channel === null) {
            throw new RuntimeException('RabbitMQ Channel 创建失败');
        }

        $messageId = bin2hex(random_bytes(16));

        $payload = [
            'message_id' => $messageId,
            'data'       => $data,
            'created_at' => date('Y-m-d H:i:s'),
        ];

        $json = json_encode(
            $payload,
            JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR
        );

        $message = new AMQPMessage(
            $json,
            [
                // RabbitMQ 持久化消息
                'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT,

                'content_type'  => 'application/json',
                'content_encoding' => 'utf-8',
                'message_id'    => $messageId,
                'timestamp'     => time(),
                'app_id'        => 'thinkphp-api',
                'type'          => 'tp8_demo',
            ]
        );

        /*
         * 使用默认交换机。
         *
         * exchange 为空字符串时,routing_key 直接写队列名称。
         */
        $this->channel->basic_publish(
            $message,
            '',
            $this->queue
        );

        return $messageId;
    }

    public function getChannel(): AMQPChannel
    {
        $this->connect();

        if ($this->channel === null) {
            throw new RuntimeException('RabbitMQ Channel 创建失败');
        }

        return $this->channel;
    }

    public function getQueue(): string
    {
        return $this->queue;
    }

    /**
     * 关闭连接。
     */
    public function close(): void
    {
        try {
            if ($this->channel !== null && $this->channel->is_open()) {
                $this->channel->close();
            }
        } catch (Throwable) {
            // 关闭阶段不继续抛出异常
        } finally {
            $this->channel = null;
        }

        try {
            if ($this->connection !== null && $this->connection->isConnected()) {
                $this->connection->close();
            }
        } catch (Throwable) {
            // 关闭阶段不继续抛出异常
        } finally {
            $this->connection = null;
        }
    }

    public function __destruct()
    {
        $this->close();
    }
}

app/api/controller/RabbitMq.php

php 复制代码
<?php

declare(strict_types=1);

namespace app\api\controller;

use app\api\service\RabbitMqService;
use JsonException;
use think\Request;
use think\Response;
use Throwable;

final class RabbitMq
{
    /**
     * 测试 RabbitMQ 是否能正常连接。
     */
    public function status(RabbitMqService $rabbitMq): Response
    {
        try {
            $rabbitMq->connect();

            return json([
                'code' => 0,
                'msg'  => 'RabbitMQ 连接成功',
                'data' => [
                    'host'  => config('rabbitmq.host'),
                    'port'  => config('rabbitmq.port'),
                    'vhost' => config('rabbitmq.vhost'),
                    'queue' => config('rabbitmq.queue'),
                ],
            ]);
        } catch (Throwable $e) {
            return json([
                'code' => 500,
                'msg'  => 'RabbitMQ 连接失败',
                'data' => [
                    'error' => $e->getMessage(),
                ],
            ], 500);
        } finally {
            $rabbitMq->close();
        }
    }

    /**
     * 向 RabbitMQ 发送一条 JSON 消息。
     */
    public function send(
        Request $request,
        RabbitMqService $rabbitMq
    ): Response {
        try {
            $input = $request->post();

            $data = [
                'id'      => isset($input['id']) ? (int) $input['id'] : 0,
                'message' => trim((string) ($input['message'] ?? '')),
            ];

            if ($data['message'] === '') {
                return json([
                    'code' => 422,
                    'msg'  => 'message 参数不能为空',
                    'data' => null,
                ], 422);
            }

            $messageId = $rabbitMq->publish($data);

            return json([
                'code' => 0,
                'msg'  => '消息发送成功',
                'data' => [
                    'message_id' => $messageId,
                    'queue'      => $rabbitMq->getQueue(),
                    'payload'    => $data,
                ],
            ]);
        } catch (JsonException $e) {
            return json([
                'code' => 422,
                'msg'  => '消息 JSON 编码失败',
                'data' => [
                    'error' => $e->getMessage(),
                ],
            ], 422);
        } catch (Throwable $e) {
            return json([
                'code' => 500,
                'msg'  => '消息发送失败',
                'data' => [
                    'error' => $e->getMessage(),
                ],
            ], 500);
        } finally {
            $rabbitMq->close();
        }
    }
}

第五步:我们要接收消息了.....; app/command/RabbitMqConsume.php

php 复制代码
<?php

declare(strict_types=1);

namespace app\command;

use app\api\service\RabbitMqService;
use JsonException;
use PhpAmqpLib\Message\AMQPMessage;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use Throwable;

final class RabbitMqConsume extends Command
{
    protected function configure(): void
    {
        $this->setName('rabbitmq:consume')
            ->setDescription('消费 RabbitMQ tp8_demo 队列消息');
    }

    protected function execute(Input $input, Output $output): int
    {
        $rabbitMq = new RabbitMqService();

        try {
            $channel = $rabbitMq->getChannel();
            $queue   = $rabbitMq->getQueue();

            // 每次只接收一条尚未确认的消息
            $channel->basic_qos(
                0,
                1,
                false
            );

            $output->writeln(
                sprintf(
                    '<info>消费者已启动,等待 %s 队列消息......</info>',
                    $queue
                )
            );

            $callback = function (AMQPMessage $message) use ($output): void {
                try {
                    $payload = json_decode(
                        $message->getBody(),
                        true,
                        512,
                        JSON_THROW_ON_ERROR
                    );

                    $output->writeln(
                        '收到消息:' . json_encode(
                            $payload,
                            JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
                        )
                    );

                    /*
                     * 在这里写实际业务逻辑。
                     *
                     * 例如:
                     * Db::name('message_log')->insert([...]);
                     */

                    $message->ack();

                    $output->writeln(
                        '<info>消息处理完成,已 ACK</info>'
                    );
                } catch (JsonException $e) {
                    $output->writeln(
                        '<error>JSON 解析失败:'
                        . $e->getMessage()
                        . '</error>'
                    );

                    // 无效 JSON 不重新入队
                    $message->reject(false);
                } catch (Throwable $e) {
                    $output->writeln(
                        '<error>业务处理失败:'
                        . $e->getMessage()
                        . '</error>'
                    );

                    // 当前示例发生异常后重新入队
                    $message->nack(false, true);
                }
            };

            $channel->basic_consume(
                $queue,
                '',
                false,
                false,
                false,
                false,
                $callback
            );

            while ($channel->is_consuming()) {
                $channel->wait();
            }

            return self::SUCCESS;
        } catch (Throwable $e) {
            $output->writeln(
                '<error>消费者错误:'
                . $e->getMessage()
                . '</error>'
            );

            return self::FAILURE;
        } finally {
            $rabbitMq->close();
        }
    }
}

在config/console.php注册命令:

php 复制代码
<?php

declare(strict_types=1);

return [
    'commands' => [
        'rabbitmq:consume' => app\command\RabbitMqConsume::class,
    ],
];

在控制台输入命令:

bash 复制代码
php think rabbitmq:consume
bash 复制代码
curl -X POST \
  "https://你的域名/api/rabbitmq/send" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "id=5022" \
  --data-urlencode "message=你好,这是 ThinkPHP 8 发送的消息"

以下是测试结果:

相关推荐
六点_dn6 小时前
RabbitMQ学习笔记-定义与作用
笔记·学习·rabbitmq
Steadfast_GG3 天前
RabbitMQ升级打怪之路(1) - RabbitMQ概述
消息队列·rabbitmq·流量削峰·消息分发·异步解耦
时代的狂4 天前
RabbitMQ 面试问答
分布式·面试·rabbitmq
半夜修仙4 天前
Spring集成邮箱功能
java·开发语言·spring boot·spring·rabbitmq·github·maven
半夜修仙5 天前
RabbitMQ的推模式和拉模式
java·分布式·中间件·rabbitmq·github·java-rabbitmq
952367 天前
RabbitMQ - 高级特性
java·spring boot·分布式·后端·rabbitmq
qiu_lovejun9987 天前
linux安装docker和redis和rabbitmq和nginx和rocketmq和kafka
linux·redis·docker·kafka·rabbitmq·rocketmq
她说可以呀8 天前
RabbitMQ Confirm与Returns模式
分布式·rabbitmq
她说可以呀8 天前
RabbitMQ重试机制
分布式·rabbitmq