关于webman框架相关的提问

chaz6chez

1. webman会在服务启动时默认启动一个httpServer,具体配置在server.php中提现;除了该服务进程,还可以通过process.php实现自定义进程;httpServer和自定义进程是否可以都抽象为process.php?理由如下:

  • web开发框架可能存在一个实例启动会需要两个http服务各自监听不同端口,或者不启动httpServer只启动如jsonRpcServer;我们可以将httpServer抽象成与process/Monitor类似的预制服务,这时候无需再自行实现httpServer,甚至官方可以提供通用的其他的预制服务模板,如:wsServer、jsonRpcServer等;
  • 我认为服务使用什么基础协议是显示性的,是基于约定的而非配置;我建议定义通用的接口来约束代码,而非配置中填写,具体如下:

使用TcpListenerInterface约束该服务为tcp基础协议

    class HttpServer implements ProcessInterface, TcpListenerInterface
    {

        /** @var App|null */
        protected $app;

        /** @inheritDoc */
        public function onWorkerStart(Worker $worker)
        {
            require_once base_path() . '/support/bootstrap.php';
            $this->app = new App($worker, Container::instance(), Log::channel('default'), app_path(), public_path());
            Http::requestClass(config('app.request_class', config('server.request_class', Request::class)));
        }

        /**
        * @param TcpConnection $connection
        * @param $data
        * @return void
        */
        public function onMessage(TcpConnection $connection, $data){
            call_user_func([$this->app, 'onMessage'], $connection, $data);
        }

        /** @inheritDoc */
        public function onWorkerStop(Worker $worker){}

        /** @inheritDoc */
        public function onWorkerReload(Worker $worker){}

        /** @inheritDoc */
        public function onWorkerExit(Worker $worker, $status, $pid){}
    }

而不是使用配置

    return [
        'listen' => 'http://0.0.0.0:8787',
        'transport' => 'tcp',
        'context' => [],
        'name' => 'webman',
        'count' => cpu_count() * 2,
        'user' => '',
        'group' => '',
        'reusePort' => false,
        'event_loop' => '',
        'stop_timeout' => 2,
        'pid_file' => runtime_path() . '/webman.pid',
        'status_file' => runtime_path() . '/webman.status',
        'stdout_file' => runtime_path() . '/logs/stdout.log',
        'log_file' => runtime_path() . '/logs/workerman.log',
        'max_package_size' => 10 * 1024 * 1024
    ];
  • helper.php源代码中的函数worker_start()有一段关于配置services的处理,我的理解是想要实现类似于微服务中的服务的关联启动;我个人认为services在webman整体框架中与process自定义进程是一个概念;比如我启动了一个名叫clearing-center的实例,这个实例分别会提供8787端口的restful-open-api、9797端口的restful-admin-api以及8000的JsonRpc-api,那么自定义进程中就会有三个对应的配置,这三个自定义进程就应该是clearing-center的三个子服务;
    foreach ($config['services'] ?? [] as $server) {
            if (!class_exists($server['handler'])) {
                echo "process error: class {$server['handler']} not exists\r\n";
                continue;
            }
            $listen = new Worker($server['listen'] ?? null, $server['context'] ?? []);
            if (isset($server['listen'])) {
                echo "listen: {$server['listen']}\n";
            }
            $instance = Container::make($server['handler'], $server['constructor'] ?? []);
            worker_bind($listen, $instance);
            $listen->listen();
    }
  • Worker:: 静态调用相关的属性我的理解是和Master相关,原本server.php中的相关内容如果在将process.php和server.php进行统一抽象后,我建议移至app.php;我认为他们应该是全局的,是所有该实例中所有进程/服务该遵守/使用的内容;

将start.php中master相关的内容移至helper.php的master_init()

function master_init(array $config){
    Worker::$pidFile = $config['pid_file'] ?? runtime_path() . '/webman.pid';
    Worker::$stdoutFile = $config['stdout_file'] ?? runtime_path() . '/logs/stdout.log';
    Worker::$logFile = $config['log_file'] ?? runtime_path() . '/logs/workerman.log';
    Worker::$eventLoopClass = $config['event_loop'] ?? '';
    TcpConnection::$defaultMaxPackageSize = $config['max_package_size'] ?? 10 * 1024 * 1024;
    if (property_exists(Worker::class, 'statusFile')) {
        Worker::$statusFile = $config['status_file'] ?? '';
    }
    if (property_exists(Worker::class, 'stopTimeout')) {
        Worker::$stopTimeout = $config['stop_timeout'] ?? 2;
    }

    Worker::$onMasterReload = function () {
        if (function_exists('opcache_get_status') and function_exists('opcache_invalidate')) {
            if ($status = opcache_get_status()) {
                if (isset($status['scripts']) && $scripts = $status['scripts']) {
                    foreach (array_keys($scripts) as $file) {
                        opcache_invalidate($file, true);
                    }
                }
            }
        }
    };
}

app.php

return [
    'debug' => true,
    'error_reporting' => E_ALL,
    'default_timezone' => 'Asia/Shanghai',
    'request_class' => Request::class,
    'public_path' => base_path() . DIRECTORY_SEPARATOR . 'public',
    'runtime_path' => base_path(false) . DIRECTORY_SEPARATOR . 'runtime',
    'controller_suffix' => '',

    'event_loop' => '',
    'stop_timeout' => 2,
    'pid_file' => runtime_path() . '/webman.pid',
    'status_file' => runtime_path() . '/webman.status',
    'stdout_file' => runtime_path() . '/logs/stdout.log',
    'log_file' => runtime_path() . '/logs/workerman.log',
    'max_package_size' => 10 * 1024 * 1024
];

2. 以上内容我都fork并实现在 https://github.com/chaz6chez/webman/tree/patch-1 ,但并未PR,原因是:

  • webman是否可以提供测试用例,以便我测试我的PR代码是否合规,也方便其他开发者PR的自审;比如 https://www.dtm.pub/other/develop.html

  • webman是否可以提供issue模板或者是开源参与的流程,用于区分是bug还是提问亦或是Request for Comments,这样其他开发者可以更好的参与进来,也可以在PR的时候关联对应的issue;

  • webman是否可以在官网描述中提供并强调一下框架的设计哲学或者说设计理念,以便开源贡献者或使用者能够更深的理解框架的内涵,统一思路;

327 1 1
1个回答

walkor

1、你的想法和我一样。目前在开发中的1.4版本可以用process.php配置多个httpserver,也在考虑将server.php合并到process.php。但是pid_file stdout_file这部分作为服务器配置放在应用配置app.php感觉也不太合适,单独放在server.php又感觉啰嗦。可能还是要有个server.php,里面只配置pid_file stdout_file等这些,不再配置http服务。1.4版本将master内容封装到support/App.php 类中了,类似你说的master_init()方法。

worker_start()有一段关于配置services的处理,这部分于微服务没有关系,是用来实现在一个进程内实现多个端口的监听。这部分用的人不多,后面可能会去掉。

2、目前没有测试用例。没有issue模版,issue大部分是使用咨询,没必要用模版。如果有小的优化改动直接发pr即可。

3、webman理念主页有介绍,总结起来就是内核尽量简单,其它靠组件、插件扩展。

  • chaz6chez 2022-08-08

    okok,worker_start那个services的部分,我想表达的意思就是我觉得services这部分没有太多作用,大部分功能都与自定义进程重叠

  • liziyu 2022-08-08

    大佬们辛苦了,这是要走“微服务”路线吗?(太菜了看不懂全凭猜)

  • chaz6chez 2022-08-08

    还是web框架啊,只是说概念上把重复的东西抽象成一起,避免冗余的设计,和微服务可能没太大关系,只是提了一嘴

🔝