hyperf 如何對AMQP訊息進行手動消費?

白狼棧發表於2022-01-10

轉發自白狼棧:檢視原文

在使用 hyperf 官方自帶的 AMQP 佇列時你會發現,不需要我們再額外啟動程式對訊息進行消費。這是因為預設情況下,使用 @Consumer 註解時,hyperf 會為我們自動建立子程式啟動消費者,並且會在子程式異常退出後,重新拉起。

來看一個簡單的例子。

1、建立producer

php bin/hyperf.php gen:amqp-producer DemoProducer

2、投遞訊息

namespace App\Controller;

use App\Amqp\Producer\DemoProducer;
use Hyperf\Amqp\Producer;
use Hyperf\Utils\ApplicationContext;

class IndexController extends AbstractController
{
    public function index()
    {
        $user = $this->request->input('user', 'Hyperf');

        $data = [
            'message' => "Hello {$user}.",
        ];
        $message = new DemoProducer($data);
        $producer = ApplicationContext::getContainer()->get(Producer::class);
        $producer->produce($message);

        return 'ok';
    }
}

3、建立消費者

php bin/hyperf.php gen:amqp-consumer DemoConsumer

4、測試

啟動專案,瀏覽器訪問 http://127.0.0.1:9501/ ,我們可以在控制檯看到列印的訊息輸出。

Array
(
    [method] => GET
    [message] => Hello Hyperf.
)
[DEBUG] 1 acked.

這個是 hyperf 自啟動程式對訊息進行消費。

現在我們想把消費程式從專案中剝離出來,用其他機器單獨進行消費。

我們參考 hyperf 自啟動的流程+command 實現。

下面先看看 hyperf 原始碼是怎麼實現自啟動消費者程式的。

首先我們注意到 Hyperf\Amqp\Listener\BeforeMainServerStartListener 這個監聽器,在 BeforeMainServerStart 或者 MainCoroutineServerStart 事件被觸發時,Hyperf\Amqp\ConsumerManager::run 方法被執行。

public function process(object $event)
{
    // Init the consumer process.
    $consumerManager = $this->container->get(ConsumerManager::class);
    $consumerManager->run();
}

Hyperf\Amqp\ConsumerManager::run 方法如下:

hyperf-consumer.png
大致步驟都在圖上進行了標註,可以看到整個過程都比較簡單。

下面我們參考這個過程,根據 command 來手動建立消費程式。

1、原 consumer 先禁用

【App\Amqp\Consumer\DemoConsumer】enable 設定 false

public function isEnable(): bool
{
    return false;
}

2、手動建立一個新的 consumer 用於測試

<?php

declare(strict_types=1);

namespace App\Amqp\Consumer;

use Hyperf\Amqp\Result;
use Hyperf\Amqp\Annotation\Consumer;
use Hyperf\Amqp\Message\ConsumerMessage;
use PhpAmqpLib\Message\AMQPMessage;

class DemoConsumer2 extends ConsumerMessage
{
    public $exchange = 'hyperf';
    public $routingKey = 'hyperf';
    public $queue = 'hyperf';

    public function consumeMessage($data, AMQPMessage $message): string
    {
        print_r($data);

        return Result::ACK;
    }

    public function isEnable(): bool
    {
        return false;
    }
}

在這個 consumer 中,我們手動指定了 exchange、routingKey 和 queue,同時禁止自啟動(enable=false)。

3、構建 command

php bin/hyperf.php gen:command DemoConsumerCommand

【App\Command\DemoConsumerCommand】程式碼如下:
<?php

declare(strict_types=1);

namespace App\Command;

use App\Amqp\Consumer\DemoConsumer;
use App\Amqp\Consumer\DemoConsumer2;
use Hyperf\Amqp\Consumer;
use Hyperf\Command\Command as HyperfCommand;
use Hyperf\Command\Annotation\Command;
use Hyperf\Di\Annotation\AnnotationCollector;
use Psr\Container\ContainerInterface;
use Hyperf\Amqp\Annotation\Consumer as ConsumerAnnotation;

/**
 * @Command
 */
#[Command]
class DemoConsumerCommand extends HyperfCommand
{
    /**
     * @var ContainerInterface
     */
    protected $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;

        parent::__construct('DemoConsumer:command');
    }

    public function configure()
    {
        parent::configure();
        $this->setDescription('手動啟動消費程式測試');
    }

    public function handle()
    {
        $consumer = $this->container->get(Consumer::class);
        $consumer->consume(make(DemoConsumer2::class));

        $this->line('ok.', 'info');
    }
}

4、重新啟動 hyperf 以及另起一個視窗啟動 command

啟動 hyperf : php bin/hyperf.php start 
啟動 command: php bin/hyperf.php DemoConsumer:command

5、測試

瀏覽器訪問 http://127.0.0.1:9501/ ,我們可以在啟動 command 的視窗看到訊息被成功輸出。

hyperf-consumer-command.png

相關文章