如何讓 Hyperf 只啟動一個服務

李銘昕發表於2021-01-08

眾所周知 Hyperf 是不支援單獨啟動某個服務的,所以有類似需求的小夥伴確實比較頭疼。

作為 Hyperf 作者,接到過很多這類需求和 PR,但都被我斃了,因為框架提供了極大的自由度,這些東西完全可以自己擴充套件。

以下我們提供一種極為友好的方式,處理這個問題。

DEMO

如何擴充套件

先讓我們閱讀一下 Symfony\Component\Console\Application 中的一段程式碼

$event = new ConsoleCommandEvent($command, $input, $output);
$e = null;

try {
    $this->dispatcher->dispatch($event, ConsoleEvents::COMMAND);

    if ($event->commandShouldRun()) {
        $exitCode = $command->run($input, $output);
    } else {
        $exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED;
    }
} catch (\Throwable $e) {
    $event = new ConsoleErrorEvent($input, $output, $e, $command);
    $this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
    $e = $event->getError();

    if (0 === $exitCode = $event->getExitCode()) {
        $e = null;
    }
}

可以看到,我們在執行具體的 Command 前,會先觸發 ConsoleCommandEvent 事件,那麼我們只需要寫一個 Listener,來觸發這個事件即可。

編寫 Listener

我們從 $event 中拿到對應的 Command,然後新增我們的需要自定義的選項,然後過載以下 InputInterface

接下來我們只需要根據對應的 Option 修改 Config 中的配置即可。

程式碼如下

<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */
namespace App\Listener;

use App\Constants\ErrorCode;
use App\Exception\BusinessException;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Event\Annotation\Listener;
use Hyperf\Event\Contract\ListenerInterface;
use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Console\Input\InputOption;

/**
 * @Listener
 */
class ConsoleCommandEventListener implements ListenerInterface
{
    /**
     * @var ContainerInterface
     */
    private $container;

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

    public function listen(): array
    {
        return [
            ConsoleCommandEvent::class,
        ];
    }

    /**
     * @param ConsoleCommandEvent $event
     */
    public function process(object $event)
    {
        if ($event instanceof ConsoleCommandEvent) {
            $command = $event->getCommand();
            $command->addOption('server', 'S', InputOption::VALUE_OPTIONAL, '需要啟動的服務');
            $input = $event->getInput();
            $input->bind($command->getDefinition());

            if ($input->getOption('server') != null) {
                $config = $this->container->get(ConfigInterface::class);
                $servers = $config->get('server.servers', []);
                $result = [];
                foreach ($servers as $server) {
                    if ($input->getOption('server') == $server['name']) {
                        $result[] = $server;
                    }
                }

                if (empty($result)) {
                    throw new BusinessException(ErrorCode::SERVER_ERROR, '服務名不存在');
                }

                $config->set('server.servers', $result);
            }
        }
    }
}

測試結果如下,可見符合我們的預期

$ php bin/hyperf.php start
[INFO] HTTP Server listening at 0.0.0.0:9502
[INFO] HTTP Server listening at 0.0.0.0:9501
^C

$ php bin/hyperf.php start -S http
[INFO] HTTP Server listening at 0.0.0.0:9501
^C

$ php bin/hyperf.php start -S http2
[INFO] HTTP Server listening at 0.0.0.0:9502
^C

寫在最後

Hyperf 是基於 Swoole 4.5+ 實現的高效能、高靈活性的 PHP 協程框架,內建協程伺服器及大量常用的元件,效能較傳統基於 PHP-FPM 的框架有質的提升,提供超高效能的同時,也保持著極其靈活的可擴充套件性,標準元件均基於 PSR 標準 實現,基於強大的依賴注入設計,保證了絕大部分元件或類都是 可替換 與 可複用 的。

框架元件庫除了常見的協程版的 MySQL 客戶端、Redis 客戶端,還為您準備了協程版的 Eloquent ORM、WebSocket 服務端及客戶端、JSON RPC 服務端及客戶端、GRPC 服務端及客戶端、Zipkin/Jaeger (OpenTracing) 客戶端、Guzzle HTTP 客戶端、Elasticsearch 客戶端、Consul 客戶端、ETCD 客戶端、AMQP 元件、Apollo 配置中心、阿里雲 ACM 應用配置管理、ETCD 配置中心、基於令牌桶演算法的限流器、通用連線池、熔斷器、Swagger 文件生成、Swoole Tracker、Blade 和 Smarty 檢視引擎、Snowflake 全域性ID生成器 等元件,省去了自己實現對應協程版本的麻煩。

Hyperf 還提供了 基於 PSR-11 的依賴注入容器、註解、AOP 面向切面程式設計、基於 PSR-15 的中介軟體、自定義程式、基於 PSR-14 的事件管理器、Redis/RabbitMQ 訊息佇列、自動模型快取、基於 PSR-16 的快取、Crontab 秒級定時任務、Translation 國際化、Validation 驗證器 等非常便捷的功能,滿足豐富的技術場景和業務場景,開箱即用。

本作品採用《CC 協議》,轉載必須註明作者和本文連結
Any fool can write code that a computer can understand. Good programmers write code that humans can understand.

相關文章