Webman自定义Process需继承support\Process并实现public onWorkerStart方法,且必须在config/process.php中注册;主进程挂起失败、工作目录异常、Windows未用windows.php启动等均会导致进程启动即退。

Webman 的自定义 Process 不是“后台脚本”,而是由主进程统一 fork、信号托管、生命周期受控的独立子进程;若它启动即退或收不到信号,大概率不是逻辑写错了,而是没过生命周期注册关。
自定义 Process 类必须继承 support\Process 并实现 onWorkerStart
Webman 不会自动识别任意类为进程,只认继承自 support\Process 且实现了 onWorkerStart 方法的类:
- 类定义必须是
class YourProcess extends support\Process,不能漏掉support\命名空间 -
onWorkerStart必须是public方法,且接收单个$worker参数(类型为Workerman\Worker) - 方法体内不能出现
exit、die或未捕获的致命错误,否则子进程立即终止 - 该类需在
config/process.php中显式注册,例如:['app\process\YourProcess', 'enable' => true]
主进程挂起失败会导致所有自定义进程“启动即消失”
Webman 主进程靠 pcntl_wait 挂起并监听 SIGCHLD 等信号;一旦挂起失败,主进程退出,所有子进程被系统回收:
- 运行
php start.php start -d后终端无日志停留,立刻返回 shell —— 这是挂起失败最典型表现 - 执行
php -m | grep pcntl,若无输出,说明 PHP 缺失pcntl扩展(Windows 下不可用,必须走windows.php启动) - 检查是否误调用了
pcntl_signal_dispatch()或pcntl_async_signals(true),它们会干扰 Webman 默认的同步信号处理链 - Linux 下可临时加日志验证:在
start.php末尾Worker::runAll()前插入file_put_contents('/tmp/master_ready', 'ok');,确认主进程确实跑到了挂起前
子进程内初始化失败常因工作目录或文件权限问题
fork 出的子进程继承父进程当前工作目录,但不保证该路径存在或可写;常见于日志、临时文件、配置加载环节:
立即学习“PHP免费学习笔记(深入)”;
- 在
onWorkerStart开头加一句:file_put_contents('/tmp/process_debug.log', "PID: ".getmypid()."\n", FILE_APPEND);,确认子进程是否真启动了 - 避免直接
chdir('/xxx'),应先if (!is_dir('/xxx')) { throw new RuntimeException('Dir not exist'); } - 打开日志文件时别裸写
fopen('log.txt', 'a'),要判断返回值:$fp = fopen('log.txt', 'a'); if (!$fp) { error_log("Can't open log: ".error_get_last()['message']); } - Windows 下必须使用
php windows.php start -d启动,否则pcntl_fork不可用,自定义进程根本不会创建
onWorkerStart 里不能阻塞,但可以启动自己的事件循环
Webman 的 Process 不提供内置事件循环,它只保证调用一次 onWorkerStart;后续逻辑你得自己扛:
- 不要写
while(true) { sleep(1); }这种低效轮询,应改用pcntl_signal+pcntl_signal_dispatch配合usleep实现信号感知的轻量等待 - 若需长期运行带 I/O 的服务(如监听 UDP、定时拉取数据),推荐用
Workerman\Timer::add注册回调,它能复用 Webman 主事件循环资源 - 禁止在
onWorkerStart中调用Worker::runAll()或启动另一个 Workerman 实例,会造成进程嵌套和信号混乱 - 协程不自动生效:即使项目启用了
webman-coroutine插件,自定义Process子进程默认仍运行在普通同步上下文中,需手动go()包裹协程逻辑
真正容易被忽略的是:自定义 Process 和 HTTP Worker 共享同一份 config/ 加载结果,但不共享内存、不共享连接池;每个子进程都是干净的 PHP 进程实例,数据库连接、Redis 客户端等都得各自初始化 —— 别指望“主进程连好了,子进程就能直接用”。



















