Workerman reload能平滑重启不中断连接,因其主进程收SIGUSR1后逐个替换子进程:老进程处理完当前请求并拒绝新连接后退出,新进程立即加载最新代码接单;但仅对onMessage等回调内动态加载的逻辑生效,静态定义、OPcache缓存或构造阶段初始化的内容不会刷新。

Workerman 的 reload 命令就是专为这个目标设计的,但前提是你的代码没写死逻辑、没绕过事件回调、也没被 OPcache 锁住旧字节码。
为什么 reload 后连接不中断?
Workerman 主进程收到 SIGUSR1 信号后,并不会杀掉整个服务,而是逐个通知 Worker 子进程“该换新代码了”。每个老进程会:继续处理完当前正在执行的 onMessage 或 onClose 回调;拒绝新连接;等所有已有连接自然关闭后才退出。新进程则立即加载最新 PHP 文件并开始接新请求——整个过程对客户端透明。
关键约束在于:这个机制只对「运行时动态加载」的业务逻辑生效。比如:
- 写在
onMessage里、通过require或include引入的业务文件,reload后会重新加载 - 但定义在 Worker 初始化阶段(如
new Worker()之后直接赋值的变量)、define()的常量、或已由class_exists()加载的类,reload不会重载
reload 失效最常见的三个原因
线上改完代码跑 php start.php reload 却没生效?大概率是以下之一:
-
opcache.enable_cli=1开着:PHP CLI 模式下 OPcache 不会自动失效,旧字节码还在跑。解决方法是加opcache_reset()到onWorkerStart,或干脆关掉该配置 - 业务逻辑写在 Worker 构造里,比如
$worker->name = 'old_logic'或直接 new 了一个单例对象:这些在reload时不重建,得走restart - 用了静态变量缓存数据(如
static $cache = []):子进程间不共享,但单个进程内 reload 后仍保留旧值,导致逻辑错乱
reload 和 restart 到底该用哪个?
别凭感觉选,看变更类型:
- 只改
onMessage/onConnect里的处理逻辑、中间件、路由匹配规则 → 用php start.php reload - 改了
start.php本身、$worker->count、监听地址、SSL 配置、或任何影响主进程初始化的参数 → 必须php start.php restart,否则新配置根本不会读取 -
php start.php reload -d是无效命令:-d只在start时生效,reload不接受该参数
验证是否真 reload 成功?别只信终端那句 “Reload success”,要查 php start.php status 输出的 PID 是否变化,或翻日志看有没有新的 worker#0 started 记录。
中间件和连接生命周期怎么配合 reload?
如果你用的是 Webman 等封装框架,中间件注册通常发生在 onWorkerStart,而这个回调在 reload 时会被重新执行——所以新中间件能立刻生效。但要注意:
- 中间件类本身如果已被 autoload 加载过,类定义不会刷新;改了中间件类的方法体,必须确保它没被 OPcache 缓存,或改用 require 动态加载
- 连接对象(
$connection)是长驻内存的,它的属性(如$connection->uid)在 reload 后依然存在,但新请求进来时会走新中间件链 - 心跳检测、超时清理这类依赖定时器的逻辑,只要定时器是在
onWorkerStart里 set 的,reload 后也会重建,无需额外操作
真正容易被忽略的是:reload 不等于“所有 PHP 状态都清空”。你得主动管理那些跨请求存活的数据,比如 Redis 连接池、数据库 PDO 实例、或自定义的全局状态容器——它们要么在 onWorkerStart 里重建,要么得自己实现 reload 时的清理钩子。

















