walkor / webman-framework

webman-framework
118 stars 61 forks source link

【RFC】为worker_start新增自定义worker class #109

Closed chaz6chez closed 2 weeks ago

chaz6chez commented 2 weeks ago

前情

目前的webman所有的启动进程都是依赖workerman\worker作为启动进程,但有少部分情况需要启动基于workerman\worker二次开发的自定义进程,如利用workbunny\webman-coroutine插件一键协程化的其他自定义进程;

workbunny\webman-coroutine实现了一个worker工具组,里面包含两个继承于workerman\worker的工具类:

以上两个类通过对workerman\worker的initWorkers方法重写,用于加载特定初始化方法

abstract class AbstractWorker extends Worker
{

    /** @inheritDoc */
    protected static function initWorkers()
    {
        foreach (static::$_workers as $worker) {
            // 加载环境
            /** @var HandlerInterface $handler */
            $handler = Factory::getCurrentHandler();
            if (!$handler) {
                $className = $worker::class;
                throw new WorkerException("Please run Factory::init or set $className::\$EventLoopClass = event_loop(). ");
            }
            $handler::initEnv();
            // 加载__init__开头的初始化方法
            $traits = class_uses($worker, false);
            foreach ($traits as $trait) {
                $methods = (new \ReflectionClass($trait))->getMethods(\ReflectionMethod::IS_PUBLIC);
                foreach ($methods as $method) {
                    $methodName = $method->getName();
                    if (str_starts_with($methodName, '__init__') and method_exists($worker, $methodName)) {
                        $worker->$methodName();
                    }
                }
            }
        }
        // 运行
        parent::initWorkers();
    }
}

初始化方法又将原有注册的onXX事件回调进行代理并协程化加载,如:

    /**
     * @return void
     */
    public function __init__workerMethods(): void
    {
        // start
        $this->_parentOnWorkerStart = $this->onWorkerStart;
        $this->onWorkerStart = function (\Workerman\Worker $worker) {
            $waitGroup = new WaitGroup();
            $waitGroup->add(1);
            new Coroutine(function () use ($worker, $waitGroup) {
                call_user_func($this->getParentOnWorkerStart(), $worker);
                $waitGroup->done();
            });
            $waitGroup->wait(-1);
        };
        // stop
        $this->_parentOnWorkerStop = $this->onWorkerStop;
        $this->onWorkerStop = function (\Workerman\Worker $worker) {
            $waitGroup = new WaitGroup();
            $waitGroup->add(1);
            new Coroutine(function () use ($worker, $waitGroup) {
                call_user_func($this->getParentOnWorkerStop(), $worker);
                $waitGroup->done();
            });
            $waitGroup->wait(-1);
        };
    }

但目前webman的worker_start函数默认是通过new Worker来进行进程加载,在这里并不能实现非侵入的处理,所以需要改造支持;

提议

为了不过于侵入webman整体框架的加载,建议在worker_start函数支持可以传入自定义Worker class,这样可以利用webman加载自定义worker的自定义进程