首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >超高性能 PHP 框架 Webman 命令行赋能事件循环 Event-Loop

超高性能 PHP 框架 Webman 命令行赋能事件循环 Event-Loop

作者头像
Tinywan
发布2026-07-01 17:44:56
发布2026-07-01 17:44:56
1340
举报
文章被收录于专栏:开源技术小栈开源技术小栈

可能很多人使用webmancommand模块的时候发现command没办法使用event-loop的能力,很多workerman的特性在command中没办法进行友好的使用,比如AsyncTcpConncetion或者其他的一定需要运行在event-loop的功能;

本分享中提供一种思路,简单的一点代码即可实现上述的功能。

Step.1Worker类实现一个执行一次便结束进程的方法,我们命名为once

代码语言:javascript
复制
<?php
declare(strict_types=1);

namespace YOUR_NAMESAPCE;

class Worker extends \Workerman\Worker
{

    /**
     * @param string|null $socketName
     * @param array $socketContext
     */
    publicfunction __construct(?string $socketName = null, array $socketContext = [])
    {
        if (!extension_loaded('posix')) {
            thrownew \RuntimeException('Please install posix extension.');
        }
        if (!extension_loaded('pcntl')) {
            thrownew \RuntimeException('Please install pcntl extension.');
        }
        parent::__construct($socketName, $socketContext);
    }

    /**
     * 运行一个一次性的进程
     *
     * @param string $name
     * @param callable $run
     * @param callable|null $stop
     * @return void
     */
    publicfunction once(string $name, callable $run, ?callable $stop = null): void
    {
        $this->name = $name;
        $this->count = 1;
        $this->onWorkerStart = function () use ($run) {
            try {
                $run();
            } finally {
                static::stopAll();
                // 向主进程发送信号,结束进程
                \posix_kill(\posix_getppid(), \SIGINT);
            }
        };
        $this->onWorkerStop = $stop;
        if (!$this->name) {
            thrownew \RuntimeException('Please set the name of the worker process through the name property.');
        }
        try {
            static::checkSapiEnv();
            static::initStdOut();
            static::init();
            static::checkPortAvailable();
            static::lock();
            static::daemonize();
            static::initWorkers();
            static::installSignal();
            static::saveMasterPid();
            static::lock(LOCK_UN);
            static::forkWorkers();
            static::resetStd();
            static::monitorWorkers();
        } catch (\Throwable $e) {
            static::log($e);
        }
    }
}

Step.2 实现一个兼容普通cli脚本的AbstractCommand

代码语言:javascript
复制
<?php
declare(strict_types=1);

namespaceYOUR_NAMESAPCE;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\ExceptionInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

abstractclass AbstractCommand extends Command
{
    /**
     * 循环类,null为普通cli程序
     *
     * @return string|null
     */
    abstractprotectedfunction eventClass(): ?string;

    /**
     * info
     *
     * @param OutputInterface $output
     * @param string $message
     * @return void
     */
    protectedfunction info(OutputInterface $output, string $message): void
    {
        $output->writeln("ℹ️ $message");
    }

    /**
     * error
     *
     * @param OutputInterface $output
     * @param string $message
     * @return int
     */
    protectedfunction error(OutputInterface $output, string $message): int
    {
        $output->writeln("❌ $message");

        returnself::FAILURE;
    }

    /**
     * success
     *
     * @param OutputInterface $output
     * @param string $message
     * @return int
     */
    protectedfunction success(OutputInterface $output, string $message): int
    {
        $output->writeln("✅ $message");

        returnself::SUCCESS;
    }

    /**
     * @override
     *
     * @param InputInterface $input
     * @param OutputInterface $output
     * @return int
     * @throws ExceptionInterface
     */
    publicfunction run(InputInterface $input, OutputInterface $output): int
    {
        if ($event = $this->eventClass()) {
            $result = 0;

            $name = strtolower(str_replace('\\','_', trim(get_called_class(), '\\')));
            $worker = new Worker();
            $worker::$pidFile = runtime_path() . "$name.pid";
            $worker::$logFile = runtime_path() . "/logs/$name.log";
            $worker->eventLoop = $event;
            $worker->once($name, function () use ($input, $output, &$result) {
                    return $result = parent::run($input, $output);
                });

            return $result;
        }

        returnparent::run($input, $output);
    }
}

Step.3 新建一个command,实现eventClass,指定其使用的event-loop

代码语言:javascript
复制
<?php

declare(strict_types=1);

namespace app\command\Debug;

use app\packages\face\builders\GetFaceEmbeddingBuilder;
use app\packages\face\workflow\FacesEmbeddingLog;
use YOUR_NAMESAPCE\AbstractCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Workerman\Events\Fiber;
use functionWorkbunny\WebmanRabbitMQ\publish;

class Test extends AbstractCommand
{
    protectedstatic $defaultName = 'debug:test';
    protectedstatic $defaultDescription = 'DEBUG';

    /** @inheritDoc */
    protectedfunction eventClass(): ?string
    {
        return Fiber::class;
    }

    /** @inheritDoc */
    protectedfunction configure()
    {
    }

    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     * @return int
     */
    protectedfunction execute(InputInterface $input, OutputInterface $output): int
    {
        $body = [
            'image_url' => $url = 'http://www.xxx.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png', // 图片地址
            'user_id'   => $userId = -1, // 用户id
            'user_type' => $type = "test",
            'prob_threshold' => 0.95, // 获取判定阈值
            'workflow'  => [
                // 人脸特征数据缓存存储/读取
                ['class' => FacesEmbeddingLog::class, 'params' => [
                    'userId'    => $userId, // 用户id
                    'userType'  => $type,
                    'imageUrl'  => $url, // 图片地址
                    'probThreshold' => 0.01, // 储存/查询阈值
                ]]
            ]
        ];
        // workbunny/rabbitmq依赖AsyncTcpConnection,一定要运行在event-loop中
        publish(new GetFaceEmbeddingBuilder(), GetFaceEmbeddingBuilder::encode($body));
        return$this->success($output, 'success');
    }
}

这时候运行php webman debug:test便能正常运行在event-loop

代码语言:javascript
复制
root@ef2d58e20604:/var/www/face# php webman debug:test
✅ success
Workerman[webman] received signal SIGINT
Workerman[webman] stopping
Workerman[webman] has been stopped

感谢兔佬的分享,原文:https://www.workerman.net/a/2004

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-06-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开源技术小栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档