环境:宝塔,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 发送的消息"
以下是测试结果:

