Workerman 启动脚本必须完全绕开 ThinkPHP CLI 生命周期,用独立脚本加载最小依赖(autoload + 手动配置),隔离初始化逻辑,显式管理进程参数与路径适配,避免资源冲突和并发问题。

Workerman 启动脚本不能依赖 ThinkPHP 的 CLI 命令生命周期
ThinkPHP5.1 和 ThinkPHP6 的 think 命令本质都是 Symfony Console 封装,执行完即退出;而 Worker::runAll() 是阻塞式、多进程 fork 模型,二者根本冲突。硬套进 Command 类(如 php think worker:server)会导致:exit 失效、daemonize 不生效、子进程共享数据库连接句柄、日志写乱、reload 失败。
兼容双版本的唯一可靠路径是:完全绕开框架命令行层,用独立 PHP 脚本启动 Workerman,只按需加载最小依赖。
- 不 require
public/index.php或think入口,避免触发 HTTP 环境检测(如报Undefined index: SERVER_NAME) - 只加载
vendor/autoload.php+ 手动引入配置(如config/app.php) - TP5.1 用
think\Loader::register()补全类自动加载(若未启用 Composer autoload) - TP6 用
think\Container::getInstance()->bind(...)显式绑定核心服务(如config、log),不依赖容器自动初始化
配置和初始化逻辑必须手动隔离,不能复用框架启动流程
TP5.1 的 App::run() 和 TP6 的 HttpServer 都会做大量中间件、路由、请求对象构建等操作,这些对 Workerman 的 TCP/HTTP/WebSocket 连接处理毫无意义,反而拖慢启动、占用内存、引发并发问题。
正确做法是在 onWorkerStart 中按需初始化业务依赖:
立即学习“PHP免费学习笔记(深入)”;
- Redis 连接:用
Predis\Client或Redis扩展单独 new,不走 TP 的cache门面(门面可能依赖请求上下文) - 数据库:TP5.1 可用
Db::connect(),TP6 推荐用think\facade\Db::connect()并显式传入配置数组,避免读取已污染的容器实例 - 模型预热:仅在主进程(
$worker->id === 0)中执行一次UserModel::with(['profile'])->find(1)类操作,防止子进程重复加载 - 日志:禁用 TP 的日志门面,直接用
file_put_contents($logFile, ...)或Monolog\Logger实例,避免多进程写同一文件
进程管理参数要显式设置,不能依赖框架默认值
TP5.1 默认无 Worker::$user、Worker::$pidFile 设置,TP6 的官方扩展也常忽略权限与路径隔离。生产环境不设 Worker::$user = 'www-data',会导致 php start.php reload 权限失败;不设 Worker::$pidFile,则无法用系统工具监控或平滑重启。
关键参数必须在脚本开头统一声明:
-
Worker::$pidFile = runtime_path() . '/workerman.pid'(TP5.1 用RUNTIME_PATH . 'workerman.pid') -
Worker::$logFile = runtime_path() . '/workerman.log'(避免写入/var/log权限问题) -
Worker::$user = 'www-data'(Linux 必设,Windows 忽略) -
Worker::$daemonize = $argv[1] ?? false === 'start'(用$argv判断,不依赖 TP 的input对象)
TP5.1 和 TP6 的路径与常量差异必须硬编码适配
TP5.1 的 APP_PATH、RUNTIME_PATH 是全局常量;TP6 废除了这些,改用函数如 app_path()、runtime_path()。混合项目里若共用同一份 Workerman 启动脚本,必须做运行时判断:
if (function_exists('app_path')) {
// TP6
$config = include app_path() . 'config/worker_http.php';
} else {
// TP5.1
$config = include APP_PATH . 'config/worker_http.php';
}
同理,vendor/autoload.php 路径一致,但 config 加载方式、日志目录拼接、甚至 Worker 类的命名空间引用(TP5.1 多用 Workerman\Worker,TP6 项目若用了 topthink/think-worker 可能引入别名)都要逐项校验。最稳妥的方式是:启动脚本不引用任何 TP 特有门面或 Facade,所有路径、配置、服务都由脚本自己 resolve。
真正难的不是语法差异,而是两个框架对“进程生命周期”和“资源隔离”的设计哲学完全不同——Workerman 要你亲手管好每一个子进程的呼吸,而 ThinkPHP 默认假设每个请求都是干净、独立、用完即焚的。一旦试图让后者替前者做决策,问题就从配置蔓延到连接泄漏、信号丢失、reload 卡死。



















