如何让 Hyperf 只启动一个服务

众所周知 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.
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 1

大佬。我最近发布的那个问题。是关于咱们dockerfile镜像文件的问题,也不算问题吧,可以在咱们的文档上面写一个问题解答。这个问题,遇到还是挺麻烦的。需要重新构建镜像啥的。其实可以,直接在镜像文件里面上注释啥的,防止小伙伴们踩坑。链接是这个。问答:php7.4,iconv,utf-8转gbk异常

4年前 评论
李铭昕 (楼主) 4年前
Fell-boy (作者) 4年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!