2019-12-12 16:24:04 +08:00
|
|
|
|
# AMQP 元件
|
2019-03-08 12:04:46 +08:00
|
|
|
|
|
2019-12-12 16:24:04 +08:00
|
|
|
|
[hyperf/amqp](https://github.com/hyperf/amqp) 是實現 AMQP 標準的元件,主要適用於對 RabbitMQ 的使用。
|
2019-03-30 22:53:32 +08:00
|
|
|
|
|
2019-12-12 16:24:04 +08:00
|
|
|
|
## 安裝
|
2019-03-30 22:53:32 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
composer require hyperf/amqp
|
|
|
|
|
```
|
2019-03-08 12:04:46 +08:00
|
|
|
|
|
2019-12-12 16:24:04 +08:00
|
|
|
|
## 預設配置
|
2019-10-14 15:16:53 +08:00
|
|
|
|
|
2023-09-02 13:57:18 +08:00
|
|
|
|
| 配置 | 型別 | 預設值 | 備註 |
|
|
|
|
|
|:----------------:|:------:|:---------:|:---------:|
|
|
|
|
|
| host | string | localhost | Host |
|
|
|
|
|
| port | int | 5672 | 埠號 |
|
|
|
|
|
| user | string | guest | 使用者名稱 |
|
|
|
|
|
| password | string | guest | 密碼 |
|
|
|
|
|
| vhost | string | / | vhost |
|
|
|
|
|
| concurrent.limit | int | 0 | 同時消費的最大數量 |
|
2019-12-12 16:24:04 +08:00
|
|
|
|
| pool | object | | 連線池配置 |
|
2021-08-15 09:55:23 +08:00
|
|
|
|
| pool.connections | int | 1 | 程序內保持的連線數 |
|
2023-09-02 13:57:18 +08:00
|
|
|
|
| params | object | | 基本配置 |
|
2019-10-14 15:16:53 +08:00
|
|
|
|
|
2019-03-19 14:52:21 +08:00
|
|
|
|
```php
|
2019-03-08 12:04:46 +08:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
return [
|
2024-01-12 09:57:51 +08:00
|
|
|
|
'enable' => true,
|
2019-03-08 12:04:46 +08:00
|
|
|
|
'default' => [
|
|
|
|
|
'host' => 'localhost',
|
|
|
|
|
'port' => 5672,
|
|
|
|
|
'user' => 'guest',
|
|
|
|
|
'password' => 'guest',
|
|
|
|
|
'vhost' => '/',
|
2019-10-14 15:16:53 +08:00
|
|
|
|
'concurrent' => [
|
|
|
|
|
'limit' => 1,
|
|
|
|
|
],
|
2019-03-08 12:04:46 +08:00
|
|
|
|
'pool' => [
|
2021-06-23 11:14:35 +08:00
|
|
|
|
'connections' => 1,
|
2019-03-08 12:04:46 +08:00
|
|
|
|
],
|
|
|
|
|
'params' => [
|
|
|
|
|
'insist' => false,
|
|
|
|
|
'login_method' => 'AMQPLAIN',
|
|
|
|
|
'login_response' => null,
|
|
|
|
|
'locale' => 'en_US',
|
|
|
|
|
'connection_timeout' => 3.0,
|
2020-04-30 17:09:23 +08:00
|
|
|
|
'read_write_timeout' => 6.0,
|
2019-03-08 12:04:46 +08:00
|
|
|
|
'context' => null,
|
|
|
|
|
'keepalive' => false,
|
2019-12-04 16:54:31 +08:00
|
|
|
|
'heartbeat' => 3,
|
2020-06-16 01:43:41 +08:00
|
|
|
|
'close_on_destruct' => false,
|
2019-03-08 12:04:46 +08:00
|
|
|
|
],
|
|
|
|
|
],
|
2019-07-04 01:49:07 +08:00
|
|
|
|
'pool2' => [
|
|
|
|
|
...
|
|
|
|
|
]
|
2019-03-08 12:04:46 +08:00
|
|
|
|
];
|
2019-03-19 14:52:21 +08:00
|
|
|
|
```
|
2019-03-08 12:04:46 +08:00
|
|
|
|
|
2021-06-23 11:14:35 +08:00
|
|
|
|
可在 `producer` 或者 `consumer` 的 `__construct` 函式中,設定不同 `pool`,例如上述的 `default` 和 `pool2`。
|
2019-07-04 01:49:07 +08:00
|
|
|
|
|
2019-12-12 16:24:04 +08:00
|
|
|
|
## 投遞訊息
|
2019-03-08 12:04:46 +08:00
|
|
|
|
|
2019-12-12 16:24:04 +08:00
|
|
|
|
使用 `gen:producer` 命令建立一個 `producer`
|
2019-10-07 23:47:41 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
2019-03-08 12:04:46 +08:00
|
|
|
|
php bin/hyperf.php gen:amqp-producer DemoProducer
|
2019-03-19 14:52:21 +08:00
|
|
|
|
```
|
2019-03-08 12:04:46 +08:00
|
|
|
|
|
2023-01-03 07:32:23 +08:00
|
|
|
|
在 DemoProducer 檔案中,我們可以修改 `#[Producer]` 註解對應的欄位來替換對應的 `exchange` 和 `routingKey`。
|
2019-12-12 16:24:04 +08:00
|
|
|
|
其中 `payload` 就是最終投遞到訊息佇列中的資料,所以我們可以隨意改寫 `__construct` 方法,只要最後賦值 `payload` 即可。
|
2019-03-08 12:04:46 +08:00
|
|
|
|
示例如下。
|
|
|
|
|
|
2023-01-03 07:32:23 +08:00
|
|
|
|
> 使用 `#[Producer]` 註解時需 `use Hyperf\Amqp\Annotation\Producer;` 名稱空間;
|
2019-10-07 23:47:41 +08:00
|
|
|
|
|
2019-03-19 14:52:21 +08:00
|
|
|
|
```php
|
2019-03-08 12:04:46 +08:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace App\Amqp\Producers;
|
|
|
|
|
|
|
|
|
|
use Hyperf\Amqp\Annotation\Producer;
|
|
|
|
|
use Hyperf\Amqp\Message\ProducerMessage;
|
|
|
|
|
use App\Models\User;
|
|
|
|
|
|
2021-12-01 16:19:47 +08:00
|
|
|
|
#[Producer(exchange: "hyperf", routingKey: "hyperf")]
|
2019-03-08 12:04:46 +08:00
|
|
|
|
class DemoProducer extends ProducerMessage
|
|
|
|
|
{
|
|
|
|
|
public function __construct($id)
|
|
|
|
|
{
|
2019-12-12 16:24:04 +08:00
|
|
|
|
// 設定不同 pool
|
2019-07-04 01:49:07 +08:00
|
|
|
|
$this->poolName = 'pool2';
|
|
|
|
|
|
2019-03-08 12:04:46 +08:00
|
|
|
|
$user = User::where('id', $id)->first();
|
|
|
|
|
$this->payload = [
|
|
|
|
|
'id' => $id,
|
|
|
|
|
'data' => $user->toArray()
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-19 14:52:21 +08:00
|
|
|
|
```
|
2019-03-08 12:04:46 +08:00
|
|
|
|
|
2022-12-27 13:37:04 +08:00
|
|
|
|
透過 DI Container 獲取 `Hyperf\Amqp\Producer` 例項,即可投遞訊息。以下例項直接使用 `ApplicationContext` 獲取 `Hyperf\Amqp\Producer` 其實並不合理,DI Container 具體使用請到 [依賴注入](zh-tw/di.md) 章節中檢視。
|
2019-03-08 12:04:46 +08:00
|
|
|
|
|
2019-03-19 14:52:21 +08:00
|
|
|
|
```php
|
2019-03-08 12:04:46 +08:00
|
|
|
|
<?php
|
|
|
|
|
use Hyperf\Amqp\Producer;
|
|
|
|
|
use App\Amqp\Producers\DemoProducer;
|
2023-04-11 15:57:32 +08:00
|
|
|
|
use Hyperf\Context\ApplicationContext;
|
2019-03-08 12:04:46 +08:00
|
|
|
|
|
|
|
|
|
$message = new DemoProducer(1);
|
|
|
|
|
$producer = ApplicationContext::getContainer()->get(Producer::class);
|
|
|
|
|
$result = $producer->produce($message);
|
|
|
|
|
|
2019-03-19 14:52:21 +08:00
|
|
|
|
```
|
2019-03-08 12:04:46 +08:00
|
|
|
|
|
2019-12-12 16:24:04 +08:00
|
|
|
|
## 消費訊息
|
2019-03-08 12:04:46 +08:00
|
|
|
|
|
2019-12-12 16:24:04 +08:00
|
|
|
|
使用 `gen:amqp-consumer` 命令建立一個 `consumer`。
|
2019-10-07 23:47:41 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
2019-03-08 12:04:46 +08:00
|
|
|
|
php bin/hyperf.php gen:amqp-consumer DemoConsumer
|
2019-03-19 14:52:21 +08:00
|
|
|
|
```
|
2019-03-08 12:04:46 +08:00
|
|
|
|
|
2023-01-03 07:32:23 +08:00
|
|
|
|
在 DemoConsumer 檔案中,我們可以修改 `#[Consumer]` 註解對應的欄位來替換對應的 `exchange`、`routingKey` 和 `queue`。
|
2019-12-12 16:24:04 +08:00
|
|
|
|
其中 `$data` 就是解析後的訊息資料。
|
2019-03-08 12:04:46 +08:00
|
|
|
|
示例如下。
|
|
|
|
|
|
2023-01-03 07:32:23 +08:00
|
|
|
|
> 使用 `#[Consumer]` 註解時需 `use Hyperf\Amqp\Annotation\Consumer;` 名稱空間;
|
2019-10-07 23:47:41 +08:00
|
|
|
|
|
2019-03-19 14:52:21 +08:00
|
|
|
|
```php
|
2019-03-08 12:04:46 +08:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace App\Amqp\Consumers;
|
|
|
|
|
|
|
|
|
|
use Hyperf\Amqp\Annotation\Consumer;
|
|
|
|
|
use Hyperf\Amqp\Message\ConsumerMessage;
|
|
|
|
|
use Hyperf\Amqp\Result;
|
2021-06-28 09:13:15 +08:00
|
|
|
|
use PhpAmqpLib\Message\AMQPMessage;
|
2019-03-08 12:04:46 +08:00
|
|
|
|
|
2021-12-01 16:19:47 +08:00
|
|
|
|
#[Consumer(exchange: "hyperf", routingKey: "hyperf", queue: "hyperf", nums: 1)]
|
2019-03-08 12:04:46 +08:00
|
|
|
|
class DemoConsumer extends ConsumerMessage
|
|
|
|
|
{
|
2024-01-06 16:04:14 +08:00
|
|
|
|
public function consumeMessage($data, AMQPMessage $message): Result
|
2019-03-08 12:04:46 +08:00
|
|
|
|
{
|
|
|
|
|
print_r($data);
|
|
|
|
|
return Result::ACK;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-19 14:52:21 +08:00
|
|
|
|
```
|
2019-03-08 12:04:46 +08:00
|
|
|
|
|
2020-01-10 16:49:10 +08:00
|
|
|
|
### 禁止消費程序自啟
|
|
|
|
|
|
2023-01-03 07:32:23 +08:00
|
|
|
|
預設情況下,使用了 `#[Consumer]` 註解後,框架會自動建立子程序啟動消費者,並且會在子程序異常退出後,重新拉起。
|
2020-01-10 16:49:10 +08:00
|
|
|
|
如果出於開發階段,進行消費者除錯時,可能會因為消費其他訊息而導致除錯不便。
|
|
|
|
|
|
2023-01-03 07:32:23 +08:00
|
|
|
|
這種情況,只需要在 `#[Consumer]` 註解中配置 `enable=false` (預設為 `true` 跟隨服務啟動)或者在對應的消費者中重寫類方法 `isEnable()` 返回 `false` 即可
|
2020-01-10 16:49:10 +08:00
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace App\Amqp\Consumers;
|
|
|
|
|
|
|
|
|
|
use Hyperf\Amqp\Annotation\Consumer;
|
|
|
|
|
use Hyperf\Amqp\Message\ConsumerMessage;
|
|
|
|
|
use Hyperf\Amqp\Result;
|
2021-06-28 09:13:15 +08:00
|
|
|
|
use PhpAmqpLib\Message\AMQPMessage;
|
2020-01-10 16:49:10 +08:00
|
|
|
|
|
2021-12-01 16:19:47 +08:00
|
|
|
|
#[Consumer(exchange: "hyperf", routingKey: "hyperf", queue: "hyperf", nums: 1, enable: false)]
|
2020-01-10 16:49:10 +08:00
|
|
|
|
class DemoConsumer extends ConsumerMessage
|
|
|
|
|
{
|
2024-01-06 16:04:14 +08:00
|
|
|
|
public function consumeMessage($data, AMQPMessage $message): Result
|
2020-01-10 16:49:10 +08:00
|
|
|
|
{
|
|
|
|
|
print_r($data);
|
|
|
|
|
return Result::ACK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function isEnable(): bool
|
|
|
|
|
{
|
|
|
|
|
return parent::isEnable();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
2019-10-07 23:47:41 +08:00
|
|
|
|
|
2020-04-30 17:09:23 +08:00
|
|
|
|
### 設定最大消費數
|
|
|
|
|
|
2023-01-03 07:32:23 +08:00
|
|
|
|
可以修改 `#[Consumer]` 註解中的 `maxConsumption` 屬性,設定此消費者最大處理的訊息數,達到指定消費數後,消費者程序會重啟。
|
2020-04-30 17:09:23 +08:00
|
|
|
|
|
2023-09-02 13:57:18 +08:00
|
|
|
|
### 設定併發消費
|
|
|
|
|
|
|
|
|
|
影響消費速率的引數有三個地方
|
|
|
|
|
|
|
|
|
|
- 可以修改 `#[Consumer]` 註解 `nums` 開啟多個消費者
|
2023-10-13 11:08:55 +08:00
|
|
|
|
- `ConsumerMessage` 基類下有一個屬性 `$qos`,可以透過重寫`$qos`中的 `prefetch_size` 或者 `prefetch_count` 的值控制每次從服務端拉取的訊息數量
|
2023-09-02 13:57:18 +08:00
|
|
|
|
- 配置檔案中的 `concurrent.limit` 引數,控制消費協程的最大數量
|
|
|
|
|
|
|
|
|
|
|
2019-12-12 16:24:04 +08:00
|
|
|
|
### 消費結果
|
2019-10-07 23:47:41 +08:00
|
|
|
|
|
2019-12-12 16:24:04 +08:00
|
|
|
|
框架會根據 `Consumer` 內的 `consume` 方法所返回的結果來決定該訊息的響應行為,共有 4 中響應結果,分別為 `\Hyperf\Amqp\Result::ACK`、`\Hyperf\Amqp\Result::NACK`、`\Hyperf\Amqp\Result::REQUEUE`、`\Hyperf\Amqp\Result::DROP`,每個返回值分別代表如下行為:
|
2019-10-07 23:47:41 +08:00
|
|
|
|
|
2019-12-12 16:24:04 +08:00
|
|
|
|
| 返回值 | 行為 |
|
2019-10-14 15:16:53 +08:00
|
|
|
|
|------------------------------|----------------------------------------------------------------------|
|
2019-12-12 16:24:04 +08:00
|
|
|
|
| \Hyperf\Amqp\Result::ACK | 確認訊息正確被消費掉了 |
|
|
|
|
|
| \Hyperf\Amqp\Result::NACK | 訊息沒有被正確消費掉,以 `basic_nack` 方法來響應 |
|
|
|
|
|
| \Hyperf\Amqp\Result::REQUEUE | 訊息沒有被正確消費掉,以 `basic_reject` 方法來響應,並使訊息重新入列 |
|
|
|
|
|
| \Hyperf\Amqp\Result::DROP | 訊息沒有被正確消費掉,以 `basic_reject` 方法來響應 |
|
2020-05-19 11:26:54 +08:00
|
|
|
|
|
2023-08-18 10:22:49 +08:00
|
|
|
|
### QOS 配置
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace App\Amqp\Consumers;
|
|
|
|
|
|
|
|
|
|
use Hyperf\Amqp\Annotation\Consumer;
|
|
|
|
|
use Hyperf\Amqp\Message\ConsumerMessage;
|
|
|
|
|
use Hyperf\Amqp\Result;
|
|
|
|
|
use PhpAmqpLib\Message\AMQPMessage;
|
|
|
|
|
|
|
|
|
|
#[Consumer(exchange: "hyperf", routingKey: "hyperf", queue: "hyperf", nums: 1)]
|
|
|
|
|
class DemoConsumer extends ConsumerMessage
|
|
|
|
|
{
|
|
|
|
|
protected ?array $qos = [
|
|
|
|
|
// AMQP 預設並沒有實現此配置。
|
|
|
|
|
'prefetch_size' => 0,
|
|
|
|
|
// 同一個消費者,最高同時可以處理的訊息數。
|
|
|
|
|
'prefetch_count' => 30,
|
|
|
|
|
// 因為 Hyperf 預設一個 Channel 只消費一個 佇列,所以 global 設定為 true/false 效果是一樣的。
|
|
|
|
|
'global' => false,
|
|
|
|
|
];
|
|
|
|
|
|
2024-01-06 16:04:14 +08:00
|
|
|
|
public function consumeMessage($data, AMQPMessage $message): Result
|
2023-08-18 10:22:49 +08:00
|
|
|
|
{
|
|
|
|
|
print_r($data);
|
|
|
|
|
return Result::ACK;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2023-11-14 09:57:28 +08:00
|
|
|
|
### 根據環境自定義消費程序數量
|
|
|
|
|
|
|
|
|
|
在 `#[Consumer]` 註解中,可以透過 `nums` 屬性來設定消費程序數量,如果需要根據不同環境來設定不同的消費程序數量,可以透過重寫 `getNums` 方法來實現,示例如下:
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
#[Consumer(
|
|
|
|
|
exchange: 'hyperf',
|
|
|
|
|
routingKey: 'hyperf',
|
|
|
|
|
queue: 'hyperf',
|
|
|
|
|
name: 'hyperf',
|
|
|
|
|
nums: 1
|
|
|
|
|
)]
|
|
|
|
|
final class DemoConsumer extends ConsumerMessage
|
|
|
|
|
{
|
|
|
|
|
public function getNums(): int
|
|
|
|
|
{
|
|
|
|
|
if (is_debug()) {
|
|
|
|
|
return 10;
|
|
|
|
|
}
|
|
|
|
|
return parent::getNums();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2021-08-30 10:46:59 +08:00
|
|
|
|
## 延時佇列
|
|
|
|
|
|
|
|
|
|
AMQP 的延時佇列,並不會根據延時時間進行排序,所以,一旦你投遞了一個延時 10s 的任務,又往這個佇列中投遞了一個延時 5s 的任務,那麼也一定會在第一個 10s 任務完成後,才會消費第二個 5s 的任務。
|
|
|
|
|
所以,需要根據時間設定不同的佇列,如果想要更加靈活的延時佇列,可以嘗試 非同步佇列(async-queue) 和 AMQP 配合使用。
|
|
|
|
|
|
|
|
|
|
另外,AMQP 需要下載 [延時外掛](https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases),並激活才能正常使用
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
wget https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases/download/3.9.0/rabbitmq_delayed_message_exchange-3.9.0.ez
|
|
|
|
|
cp rabbitmq_delayed_message_exchange-3.9.0.ez /opt/rabbitmq/plugins/
|
|
|
|
|
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 生產者
|
|
|
|
|
|
|
|
|
|
使用 `gen:amqp-producer` 命令建立一個 `producer`。這裡舉例 `direct` 型別,其他型別如 `fanout`、`topic`,改生產者和消費者中的 `type` 即可。
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
php bin/hyperf.php gen:amqp-producer DelayDirectProducer
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
在 DelayDirectProducer 檔案中,加入`use ProducerDelayedMessageTrait;`,示例如下:
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Amqp\Producer;
|
|
|
|
|
|
|
|
|
|
use Hyperf\Amqp\Annotation\Producer;
|
|
|
|
|
use Hyperf\Amqp\Message\ProducerDelayedMessageTrait;
|
|
|
|
|
use Hyperf\Amqp\Message\ProducerMessage;
|
|
|
|
|
use Hyperf\Amqp\Message\Type;
|
|
|
|
|
|
2021-12-01 16:19:47 +08:00
|
|
|
|
#[Producer]
|
2021-08-30 10:46:59 +08:00
|
|
|
|
class DelayDirectProducer extends ProducerMessage
|
|
|
|
|
{
|
|
|
|
|
use ProducerDelayedMessageTrait;
|
|
|
|
|
|
2023-08-15 15:16:14 +08:00
|
|
|
|
protected string $exchange = 'ext.hyperf.delay';
|
2021-08-30 10:46:59 +08:00
|
|
|
|
|
2023-08-15 15:16:14 +08:00
|
|
|
|
protected Type|string $type = Type::DIRECT;
|
2021-08-30 10:46:59 +08:00
|
|
|
|
|
2023-08-15 15:16:14 +08:00
|
|
|
|
protected array|string $routingKey = '';
|
2021-08-30 10:46:59 +08:00
|
|
|
|
|
|
|
|
|
public function __construct($data)
|
|
|
|
|
{
|
|
|
|
|
$this->payload = $data;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
2023-08-15 15:16:14 +08:00
|
|
|
|
|
2021-08-30 10:46:59 +08:00
|
|
|
|
### 消費者
|
|
|
|
|
|
|
|
|
|
使用 `gen:amqp-consumer` 命令建立一個 `consumer`。
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
php bin/hyperf.php gen:amqp-consumer DelayDirectConsumer
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
在 `DelayDirectConsumer` 檔案中,增加引入`use ProducerDelayedMessageTrait, ConsumerDelayedMessageTrait;`,示例如下:
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace App\Amqp\Consumer;
|
|
|
|
|
|
|
|
|
|
use Hyperf\Amqp\Annotation\Consumer;
|
|
|
|
|
use Hyperf\Amqp\Message\ConsumerDelayedMessageTrait;
|
|
|
|
|
use Hyperf\Amqp\Message\ConsumerMessage;
|
|
|
|
|
use Hyperf\Amqp\Message\ProducerDelayedMessageTrait;
|
|
|
|
|
use Hyperf\Amqp\Message\Type;
|
|
|
|
|
use Hyperf\Amqp\Result;
|
|
|
|
|
use PhpAmqpLib\Message\AMQPMessage;
|
|
|
|
|
|
2021-12-01 16:19:47 +08:00
|
|
|
|
#[Consumer(nums: 1)]
|
2021-08-30 10:46:59 +08:00
|
|
|
|
class DelayDirectConsumer extends ConsumerMessage
|
|
|
|
|
{
|
|
|
|
|
use ProducerDelayedMessageTrait;
|
|
|
|
|
use ConsumerDelayedMessageTrait;
|
|
|
|
|
|
2023-08-15 15:16:14 +08:00
|
|
|
|
protected string $exchange = 'ext.hyperf.delay';
|
2021-08-30 10:46:59 +08:00
|
|
|
|
|
2023-08-15 15:16:14 +08:00
|
|
|
|
protected string $queue = 'queue.hyperf.delay';
|
2021-08-30 10:46:59 +08:00
|
|
|
|
|
2023-08-15 15:16:14 +08:00
|
|
|
|
protected Type|string $type = Type::DIRECT; //Type::FANOUT;
|
2021-08-30 10:46:59 +08:00
|
|
|
|
|
2023-08-15 15:16:14 +08:00
|
|
|
|
protected array|string $routingKey = '';
|
2021-08-30 10:46:59 +08:00
|
|
|
|
|
2024-01-06 16:04:14 +08:00
|
|
|
|
public function consumeMessage($data, AMQPMessage $message): Result
|
2021-08-30 10:46:59 +08:00
|
|
|
|
{
|
|
|
|
|
var_dump($data, 'delay+direct consumeTime:' . (microtime(true)));
|
|
|
|
|
return Result::ACK;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 生產延時訊息
|
|
|
|
|
|
|
|
|
|
> 以下是在 Command 中演示如何使用,具體用法請以實際為準
|
|
|
|
|
|
|
|
|
|
使用 `gen:command DelayCommand` 命令建立一個 `DelayCommand`。如下:
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace App\Command;
|
|
|
|
|
|
|
|
|
|
use App\Amqp\Producer\DelayDirectProducer;
|
|
|
|
|
//use App\Amqp\Producer\DelayFanoutProducer;
|
|
|
|
|
//use App\Amqp\Producer\DelayTopicProducer;
|
|
|
|
|
use Hyperf\Amqp\Producer;
|
|
|
|
|
use Hyperf\Command\Annotation\Command;
|
|
|
|
|
use Hyperf\Command\Command as HyperfCommand;
|
2023-04-11 15:57:32 +08:00
|
|
|
|
use Hyperf\Context\ApplicationContext;
|
2021-08-30 10:46:59 +08:00
|
|
|
|
use Psr\Container\ContainerInterface;
|
|
|
|
|
|
2021-12-01 16:19:47 +08:00
|
|
|
|
#[Command]
|
2021-08-30 10:46:59 +08:00
|
|
|
|
class DelayCommand extends HyperfCommand
|
|
|
|
|
{
|
2021-12-01 16:19:47 +08:00
|
|
|
|
protected ContainerInterface $container;
|
2021-08-30 10:46:59 +08:00
|
|
|
|
|
|
|
|
|
public function __construct(ContainerInterface $container)
|
|
|
|
|
{
|
|
|
|
|
$this->container = $container;
|
|
|
|
|
|
|
|
|
|
parent::__construct('demo:command');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function configure()
|
|
|
|
|
{
|
|
|
|
|
parent::configure();
|
|
|
|
|
$this->setDescription('Hyperf Demo Command');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function handle()
|
|
|
|
|
{
|
|
|
|
|
//1.delayed + direct
|
|
|
|
|
$message = new DelayDirectProducer('delay+direct produceTime:'.(microtime(true)));
|
|
|
|
|
//2.delayed + fanout
|
|
|
|
|
//$message = new DelayFanoutProducer('delay+fanout produceTime:'.(microtime(true)));
|
|
|
|
|
//3.delayed + topic
|
|
|
|
|
//$message = new DelayTopicProducer('delay+topic produceTime:' . (microtime(true)));
|
|
|
|
|
$message->setDelayMs(5000);
|
|
|
|
|
$producer = ApplicationContext::getContainer()->get(Producer::class);
|
|
|
|
|
$producer->produce($message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
```
|
2023-08-15 15:16:14 +08:00
|
|
|
|
|
2021-08-30 10:46:59 +08:00
|
|
|
|
執行命令列生產訊息
|
2023-08-15 15:16:14 +08:00
|
|
|
|
|
2021-08-30 10:46:59 +08:00
|
|
|
|
```
|
|
|
|
|
php bin/hyperf.php demo:command
|
|
|
|
|
```
|
|
|
|
|
|
2020-05-19 11:26:54 +08:00
|
|
|
|
## RPC 遠端過程呼叫
|
|
|
|
|
|
2022-12-27 13:37:04 +08:00
|
|
|
|
除了典型的訊息佇列場景,我們還可以透過 AMQP 來實現 RPC 遠端過程呼叫,本元件也為這個實現提供了對應的支援。
|
2020-05-19 11:26:54 +08:00
|
|
|
|
|
|
|
|
|
### 建立消費者
|
|
|
|
|
|
2022-12-27 13:37:04 +08:00
|
|
|
|
RPC 使用的消費者,與典型訊息佇列場景的消費者實現基本無差,唯一的區別是需要透過呼叫 `reply` 方法返回資料給生產者。
|
2020-05-19 11:26:54 +08:00
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace App\Amqp\Consumer;
|
|
|
|
|
|
|
|
|
|
use Hyperf\Amqp\Annotation\Consumer;
|
|
|
|
|
use Hyperf\Amqp\Message\ConsumerMessage;
|
|
|
|
|
use Hyperf\Amqp\Result;
|
|
|
|
|
use PhpAmqpLib\Message\AMQPMessage;
|
|
|
|
|
|
2021-12-01 16:19:47 +08:00
|
|
|
|
#[Consumer(exchange: "hyperf", routingKey: "hyperf", queue: "rpc.reply", name: "ReplyConsumer", nums: 1, enable: true)]
|
2020-05-19 11:26:54 +08:00
|
|
|
|
class ReplyConsumer extends ConsumerMessage
|
|
|
|
|
{
|
2024-01-06 16:04:14 +08:00
|
|
|
|
public function consumeMessage($data, AMQPMessage $message): Result
|
2020-05-19 11:26:54 +08:00
|
|
|
|
{
|
|
|
|
|
$data['message'] .= 'Reply:' . $data['message'];
|
|
|
|
|
|
|
|
|
|
$this->reply($data, $message);
|
|
|
|
|
|
|
|
|
|
return Result::ACK;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 發起 RPC 呼叫
|
|
|
|
|
|
2022-12-27 13:37:04 +08:00
|
|
|
|
作為生成者發起一次 RPC 遠端過程呼叫也非常的簡單,只需透過依賴注入容器獲得 `Hyperf\Amqp\RpcClient` 物件並呼叫其中的 `call` 方法即可,返回的結果是消費者 reply 的資料,如下所示:
|
2020-05-19 11:26:54 +08:00
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
<?php
|
|
|
|
|
use Hyperf\Amqp\Message\DynamicRpcMessage;
|
|
|
|
|
use Hyperf\Amqp\RpcClient;
|
2023-04-11 15:57:32 +08:00
|
|
|
|
use Hyperf\Context\ApplicationContext;
|
2020-05-19 11:26:54 +08:00
|
|
|
|
|
|
|
|
|
$rpcClient = ApplicationContext::getContainer()->get(RpcClient::class);
|
|
|
|
|
// 在 DynamicRpcMessage 上設定與 Consumer 一致的 Exchange 和 RoutingKey
|
|
|
|
|
$result = $rpcClient->call(new DynamicRpcMessage('hyperf', 'hyperf', ['message' => 'Hello Hyperf']));
|
|
|
|
|
|
|
|
|
|
// $result:
|
|
|
|
|
// array(1) {
|
|
|
|
|
// ["message"]=>
|
|
|
|
|
// string(18) "Reply:Hello Hyperf"
|
|
|
|
|
// }
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 抽象 RpcMessage
|
|
|
|
|
|
2022-12-27 13:37:04 +08:00
|
|
|
|
上面的 RPC 呼叫過程是直接透過 `Hyperf\Amqp\Message\DynamicRpcMessage` 類來完成 Exchange 和 RoutingKey 的定義,並傳遞訊息資料,在生產專案的設計上,我們可以對 RpcMessage 進行一層抽象,以統一 Exchange 和 RoutingKey 的定義。
|
2020-05-19 11:26:54 +08:00
|
|
|
|
|
|
|
|
|
我們可以建立對應的 RpcMessage 類如 `App\Amqp\FooRpcMessage` 如下:
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
<?php
|
|
|
|
|
use Hyperf\Amqp\Message\RpcMessage;
|
|
|
|
|
|
|
|
|
|
class FooRpcMessage extends RpcMessage
|
|
|
|
|
{
|
|
|
|
|
|
2023-08-15 15:16:14 +08:00
|
|
|
|
protected string $exchange = 'hyperf';
|
2020-05-19 11:26:54 +08:00
|
|
|
|
|
2023-08-15 15:16:14 +08:00
|
|
|
|
protected array|string $routingKey = 'hyperf';
|
2020-05-19 11:26:54 +08:00
|
|
|
|
|
|
|
|
|
public function __construct($data)
|
|
|
|
|
{
|
|
|
|
|
// 要傳遞資料
|
|
|
|
|
$this->payload = $data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-06-16 01:43:41 +08:00
|
|
|
|
這樣我們進行 RPC 呼叫時,只需直接傳遞 `FooRpcMessage` 例項到 `call` 方法即可,無需每次呼叫時都去定義 Exchange 和 RoutingKey。
|