⚡ ThinkPlugsWorker
ThinkPlugsWorker 是基于 Workerman 4.x / 5.x 的基础服务插件,提供 xadmin:worker 命令、HTTP 服务启动和自定义 Workerman / GatewayWorker 进程配置能力。
当前 composer.json 要求 PHP >7.1,依赖 workerman/workerman ^5.0|^4.1、think-install 和 think-library。默认初始化配置只包含 HTTP 服务所需的 host、port、context、classes、callable 与 worker;自定义服务需要自行追加 worker.customs 配置。
🔐 PHP 安全边界
ThinkPlugsWorker 是 PHP 常驻进程。路由、中间件、Request、Session、Cookie 和数据库状态隔离修复用于避免应用状态跨请求污染,属于 ThinkAdmin 应用层生命周期修复,不能替代 PHP 引擎安全升级。
针对 CVE-2026-109618 和 CVE-2026-109619,生产环境应优先使用 PHP 8.4.27,最低安全基线为 PHP 8.2.35。PHP 7.x 仅在取得可追溯的第三方回补证据并完成独立验收后使用,具体版本和证据要求见 PHP 安全版本与维护建议。
升级 PHP 包后必须完整停止并启动 Worker,使主进程和子进程加载新的 PHP 二进制;只执行应用层 reload 不能证明 PHP 引擎已经升级。FPM、CLI 和 Worker 必须分别检查版本、SAPI、扩展和业务回归结果。
🔄 常驻 HTTP 请求生命周期
ThinkPlugsWorker 的 HTTP 服务只创建一次 ThinkApp,随后由同一个 PHP 进程处理多次请求。当前实现通过“启动基线 + 请求前恢复 + 请求后清理”控制复用范围,不会为每个请求重新创建完整的 App。
Worker 启动阶段
HttpServer::onWorkerStart() 按以下顺序初始化:
- 创建
ThinkApp,绑定 Worker 专用的 Request 和 Cookie 实现。 - 执行
RuntimeService::init($app)->initialize(),加载项目和插件运行环境。 - 首次创建
route容器,加载启动阶段的基础路由和中间件。 - 调用
ThinkApp::captureWorkerState(),保存global、app、route、controller四类 Middleware 队列快照;多应用中间件也在此时建立启动基线。 - 从配置初始化 Workerman Session/Cookie 静态参数,其中
session.expire会按整数使用。 - 注册数据库保活定时器,减少长时间空闲连接失效的影响。
启动阶段的 route 只用于形成基础状态,不能作为后续请求的共享路由实例。每次请求都会删除它,由下一次 Http::run() 按当前应用重新创建。
消息入口的两个旁路
HttpServer::onMessage() 会先处理两类不进入 ThinkAdmin 应用生命周期的请求:
public/下的真实静态文件直接返回文件响应,不执行ThinkApp::worker()的请求清理流程。worker.callable返回true时,认为自定义回调已经完成响应,也不会继续进入框架请求管线。
自定义回调如果保存了跨请求对象或修改了全局变量,需要自行恢复;需要 ThinkAdmin Request、Session、路由和中间件隔离的 HTTP 请求应交给默认框架处理。
每次请求的入口恢复
进入 ThinkApp::worker() 后首先调用 resetWorkerState():
- 清空当前插件标记
CurrentPluginCode。 - 恢复四类 Middleware 队列,防止私有路由或控制器中间件追加到后续公开请求。
- 调用多应用中间件的
resetApplicationState(),恢复配置、配置钩子、容器绑定、服务、事件和 HTTP 应用上下文基线;基础 Request、Session、Cookie、DB、Cache、Log、View 实例按现有清理逻辑继续复用。 - 删除
route容器实例,使本次请求重新加载当前应用路由。
随后清理 View 和数据库查询计时,清空并设置当前 Session ID,使用 ThinkRequest::withWorkerRequest() 将 Workerman 请求转换为 ThinkPHP Request,再创建当前响应的 Cookie 对象。
请求转换与超全局边界
Worker 请求会同步到 ThinkPHP Request 和 PHP 超全局,确保常驻模式与 FPM 的输入行为一致:
| 输入 | 当前行为 |
|---|---|
| GET、POST、Cookie、上传文件 | 同时写入 Request 对象以及 $_GET、$_POST、$_COOKIE、$_FILES |
$_REQUEST | 按 POST + GET + Cookie 合并,保持当前项目的键覆盖顺序 |
| 请求头 | 普通头转换为 HTTP_*,Content-Type 和 Content-Length 保留 PHP 约定的键名 |
| Host、端口、协议 | 根据代理头和连接信息推导,端口转为整数,443 默认使用 https |
0 值 | 过滤空值时保留字符串 "0",避免合法端口和请求头被误删 |
| 原始请求体 | 写入 Request 输入和 HTTP_RAW_POST_DATA 兼容变量 |
请求结束或异常时,ThinkRequest::clearWorkerGlobals() 将上述变量恢复为 Worker 启动基线;下一请求不会继承上一次请求的 Query、POST、Cookie、上传文件、代理头或原始请求体。
正常结束与异常清理
正常请求执行 Http::run()、写入响应并调用 Http::end()。异常路径由 Worker 异常处理器返回 500 响应,随后仍执行统一 finally 清理:
- 清空多余输出缓冲;异常路径先保存日志,保证当前上下文仍可供日志监听器读取。
- 恢复 Request 超全局、Session、Cookie 响应状态和 DB 查询计时。
- 删除 View 实例,恢复 Middleware、多应用状态并删除 Route 实例。
- 清空进程级插件标记。
清理失败不会覆盖原始异常响应;下一次请求入口仍会再次执行基线恢复。当前实现没有默认调用 gc_collect_cycles(),也没有每请求重建完整 App,以避免把固定延迟和额外初始化成本引入所有请求。
🧭 FPM 与 Worker 的边界
FPM 每次请求由 PHP-FPM 处理独立请求生命周期,通常不需要 Worker 的 Middleware 快照和 Route 重建。MultAccess 只在检测到应用提供 Worker 状态恢复能力时建立快照,因此 FPM 不承担这部分反射和复制开销。
两种模式仍需分别验收:
- FPM 重点检查真实
PHP_SAPI=fpm-fcgi、请求输入、Session、配置覆盖、数据库事务和异常后的同一 FPM worker 行为。 - Worker 重点检查
PHP_SAPI=cli、同一 PID 连续请求、Route 规则数量、Middleware 队列数量、请求超全局和异常后的状态恢复。 - PHP 二进制升级后必须完整重启 FPM pool 和 Worker;应用层
reload不能代替 PHP 二进制重启。
🧪 当前实现的验收入口
推荐在隔离 Docker 矩阵中运行项目验收:
# 在 ThinkAdminDeveloper 根目录执行
bash tests/docker/run.sh 8.2 8.4 full该入口会分别执行 SQLite/ MySQL HTTP 生命周期检查和 SQLite Worker 生命周期检查。Worker 专项覆盖:
- 私有路由中间件执行后,公开路由连续请求不被污染;顺序覆盖
private → public → public和public → private → public。 - 私有控制器中间件只作用于配置了
only的操作,公开操作不残留控制器中间件。 - 私有路由正常、异常后,公开路由仍返回原始边界。
- 至少 100 次请求保持同一 Worker PID,
getGroup()->getRules()、Route Middleware 数量和 Controller Middleware 数量保持恒定。 - 记录每次
memory_get_usage(true);当前脚本硬断言规则和队列数量恒定,内存的有界波动与持续线性增长需结合压测输出继续分析。 - 同一请求会话、不同请求会话、Session、Cookie、数据库查询计时和 PHP 超全局之间不串值。
tests/check-lifecycle.php ... worker 的规则数量判断使用 getGroup()->getRules();不能用 getRuleList() 作为泄漏判断依据。PHP 官方 CVE PHPT 与本项目 Worker 状态验收属于两组独立证据,不能相互替代,详见 PHP 安全版本与维护建议。
⚙️ 配置数值类型
环境变量通过 env() 读取时原始值可能是字符串,当前模板已对会传入整数参数的配置显式转换:
| 环境变量 | 配置结果 | 默认值 | 说明 |
|---|---|---|---|
SESSION_EXPIRE | int | 7200 | Session 过期时间,Worker 初始化时作为 Session lifetime 使用 |
CACHE_REDIS_PORT | int | 6379 | Redis 连接端口 |
CACHE_REDIS_SELECT | int | 0 | Redis 数据库编号 |
转换采用 PHP 原生 (int) 语义,包含字符串 "0" 的零值;全局 env() 规则和其他字符串配置不变。MySQL hostport 等支持逗号分隔多端口的配置继续保留字符串,不能为了统一转型而破坏多端口能力。
安装器不会覆盖已经存在的项目配置。已部署项目如果使用环境变量覆盖上述配置,应同步更新现有 config/session.php、config/cache.php,再重启 FPM、CLI 常驻任务和 Worker。
🚀 主要功能
- HTTP 服务: 直接启动 ThinkAdmin 项目,无需 Nginx 或 Apache
- 自定义服务: 通过自行追加的
customs配置启动 text、WebSocket、Gateway、Register、Business 等服务 - 进程管理: Linux 支持 start、stop、reload、restart、status 和 connections 操作;Windows 支持 start、stop 和 status 操作
- 后台运行: 支持
--daemon参数,日志、PID、状态文件可通过worker配置覆盖,默认写入safefile/worker/ - 进程重载: Linux 环境可通过
reload指令触发 Workerman 进程重载 - 多进程运行: 通过 Workerman
worker.count配置服务进程数量
⚙️ 运行方式
Workerman 模式
- 默认运行方式,基于 Workerman 4.x / 5.x
- 支持 HTTP 协议直接运行 ThinkAdmin
- 无需额外配置 Web 服务器
- 支持按
worker配置设置进程名称、数量和回调
Gateway 模式
- 需要单独安装 Gateway 组件
- 支持 WebSocket 等实时通信协议
- 适用于实时应用场景
- 提供消息广播功能
自定义协议模式
- 支持自定义通信协议
- 可扩展多种业务场景
- 灵活配置服务参数
- 支持多种监听方式
📋 配置说明
默认配置
- 根配置参数启动 HTTP 服务进程
- 用于运行 ThinkAdmin v6 程序
- 支持自定义端口和域名配置
自定义协议
- 使用
customs配置定义其他协议;该键不是默认模板的一部分,需要项目自行追加 - 通过
--custom name启动对应服务 - 支持多种通信协议同时运行
加入我们
我们的代码仓库已移至 Github,而 Gitee 则仅作为国内镜像仓库,方便广大开发者获取和使用。若想提交 PR 或 ISSUE 请在 ThinkAdminDeveloper 仓库进行操作,如果在其他仓库操作或提交问题将无法处理!。
标准安装
**安装前建议尝试更新所有组件**
composer update --optimize-autoloader
composer require zoujingli/think-plugs-worker --optimize-autoloader定制安装
- 复制插件 ThinkPlugsWorker 所有源代码到
plugin/think-plugs-worker目录。 - 修改插件
composer.json文件追加version配置项,插件 ThinkPlugsWorker 的完整配置如下:
{
"type": "think-admin-plugin",
"name": "zoujingli/think-plugs-worker",
"version": "1.0.0",
"license": "Apache-2.0",
"homepage": "https://thinkadmin.top",
"description": "Workerman HTTP and custom process service plugin for ThinkAdmin",
"authors": [
{
"name": "Anyon",
"email": "zoujingli@qq.com"
}
],
"require": {
"php": ">7.1",
"workerman/workerman": "^5.0|^4.1",
"zoujingli/think-install": "^1.0|@dev",
"zoujingli/think-library": "^6.1|@dev"
},
"autoload": {
"psr-4": {
"plugin\\worker\\": "src"
}
},
"extra": {
"think": {
"services": [
"plugin\\worker\\Service"
]
},
"config": {
"type": "service",
"name": "基于 Workerman 的通信服务",
"document": "https://thinkadmin.top/plugin/think-plugs-worker.html",
"license": [
"Apache-2.0"
]
},
"plugin": {
"init": {
"stc/worker.php": "config/worker.php"
},
"event": {
"src/Script.php": "plugin\\worker\\Script"
}
}
}
}- 修改项目根
composer.json配置,增加 ThinkPlugsWorker 插件本地化路径配置,完整配置如下:
{
"type": "project",
"name": "zoujingli/thinkadmin",
"license": "MIT",
"homepage": "https://thinkadmin.top",
"description": "ThinkAdmin plugin aggregation and distribution project",
"keywords": [
"ThinkAdmin",
"ThinkLibrary",
"WeChatDeveloper"
],
"authors": [
{
"name": "Anyon",
"email": "zoujingli@qq.com"
}
],
"require": {
"php": ">7.1",
"ext-gd": "*",
"ext-json": "*",
"ext-openssl": "*",
"zoujingli/think-plugs-admin": "^1.0",
"zoujingli/think-plugs-worker": "^1.0"
},
"repositories": [
{
"type": "path",
"url": "plugin/think-plugs-worker"
}
],
"config": {
"sort-packages": true,
"allow-plugins": {
"zoujingli/think-install": true
}
}
}- 在项目根目录终端执行
composer update完成安装。
卸载插件
composer remove zoujingli/think-plugs-worker配置参数
配置文件 config/worker.php
默认模板来自 plugin/think-plugs-worker/stc/worker.php。如下 customs 示例是可选扩展配置,只有使用 --custom name 启动非默认服务时才需要添加。
return [
// 服务监听地址
'host' => '127.0.0.1',
// 服务监听端口
'port' => 2346,
// 套接字上下文选项
'context' => [],
// 高级自定义服务类
'classes' => '',
// 消息请求回调处理
'callable' => null,
// 服务进程参数配置
'worker' => [
// 进程名称
"name" => 'ThinkAdmin',
// 进程数量
'count' => 4,
],
// 自定义服务配置(可选)
'customs' => [
// 自定义 text 服务
'text' => [
// 进程类型(Workerman|Gateway|Register|Business)
'type' => 'Workerman',
// 监听地址(<协议>://<地址>:<端口>)
'listen' => 'text://0.0.0.0:8685',
// 高级自定义服务类
'classes' => '',
// 套接字上下文选项
'context' => [],
// 服务进程参数配置
'worker' => [
//'name' => 'TextTest',
// onWorkerStart => [class,method]
// onWorkerReload => [class,method]
// onConnect => [class,method]
// onBufferFull => [class,method]
// onBufferDrain => [class,method]
// onError => [class,method]
// 设置连接的 onMessage 回调
'onMessage' => function ($connection, $data) {
$connection->send("hello world");
}
]
],
// 自定义 websocket 服务
'websocket' => [
// 进程类型(Workerman|Gateway|Register|Business)
'type' => 'Workerman',
// 监听地址(<协议>://<地址>:<端口>)
'listen' => 'websocket://0.0.0.0:8686',
// 高级自定义服务类
'classes' => '',
// 套接字上下文选项
'context' => [],
// 服务进程参数配置
'worker' => [
//'name' => 'TextTest',
// onWorkerStart => [class,method]
// onWorkerReload => [class,method]
// onConnect => [class,method]
// onBufferFull => [class,method]
// onBufferDrain => [class,method]
// onError => [class,method]
// 设置连接的 onMessage 回调
'onMessage' => function ($connection, $data) {
$connection->send("hello world");
}
]
],
// 自定义 Gateway 服务
'gateway' => [
// 进程类型(Workerman|Gateway|Register|Business)
'type' => 'Gateway',
// 监听地址(<协议>://<地址>:<端口>)
'listen' => 'websocket://127.0.0.1:8689',
// 高级自定义服务类
'classes' => '',
// 套接字上下文选项
'context' => [],
// 服务进程参数配置
'worker' => [
// 进程名称
"name" => 'Gateway',
// 进程数量
"count" => 4,
// 心跳发送时间,针对客户端
'pingInterval' => 10,
// 心跳容错次数,针对客户端
'pingNotResponseLimit' => 0,
// 心跳包内容,针对客户端
'pingData' => '{"type":"ping"}',
// 服务器内网IP
"lanIp" => '127.0.0.1',
// Business 回复 Gateway 端口
"startPort" => 2000,
// 注册服务地址,与 Register 进程对应
"registerAddress" => '127.0.0.1:1236',
// 进程启动回调
"onWorkerStart" => function () {
echo "Gateway onWorkerStart" . PHP_EOL;
},
// 进程停止回调
"onWorkerStop" => function () {
echo "Gateway onWorkerStop" . PHP_EOL;
}
]
],
// 自定义 Register 服务
'register' => [
// 进程类型(Workerman|Gateway|Register|Business)
'type' => 'Register',
// 监听地址(<协议>://<地址>:<端口>)
// 注意:别改这里的协议,只支持 text 协议
'listen' => 'text://127.0.0.1:1236'
],
// 自定义 Business 服务
'business' => [
// 进程类型(Workerman|Gateway|Register|Business)
'type' => 'Business',
// 高级自定义服务类
'classes' => '',
// 服务进程参数配置
'worker' => [
// 进程名称
"name" => 'Business',
// 进程数量
"count" => 4,
// 注册服务地址,与 Register 进程对应
"registerAddress" => '127.0.0.1:1236',
// 业务处理类
"eventHandler" => Events::class,
// 进程启动回调
"onWorkerStart" => function () {
echo "Business onWorkerStart" . PHP_EOL;
},
// 进程停止回调
"onWorkerStop" => function () {
echo "Business onWorkerStart" . PHP_EOL;
}
]
],
],
];
/**
* 业务处理类
* @class Events
*/
class Events
{
/**
* 业务进程启动
* @param $businessWorker
* @return void
*/
public static function onWorkerStart($businessWorker)
{
echo "Events WorkerStart\n";
}
/**
* 有消息时触发该方法
* @param int $clientid 发消息的client_id
* @param mixed $message 消息
* @throws \Exception
*/
public static function onMessage($clientid, $message)
{
// 群聊,转发请求给其它所有的客户端
\GatewayWorker\Lib\Gateway::sendToAll("Message By Events : {$message}");
}
}使用方法
在命令行启动服务端
#========= 启动参数配置 =========
**后台方式运行 --daemon 或 -d**
**指定监听域名 --host 127.0.0.1 或 -H 127.0.0.1**
**指定监听端口 --port 2346 或 -p 2346**
**启动指定服务 --custom websocket 或 -c websocket**
# 通过 Workerman 方式,启动默认 Http 服务
php think xadmin:worker
# 通过 Workerman 方式,启动自定义 text 服务,注意 text 为 customs 配置项
php think xadmin:worker --custom text
# 通过 Workerman 方式,启动自定义 WebSocket 服务,注意 websocket 为 customs 配置项
php think xadmin:worker --custom websocket
# 通过 Gateway 方式,需要同时启动三个进程,另外还需要安装 workerman/gateway-worker 依赖包。
# 具体业务处理逻辑写在 business 绑定的 Events 中,了解更新多配置请阅读 Workerman 官方文档。
php think xadmin:worker --custom register
php think xadmin:worker --custom gateway
php think xadmin:worker --custom business然后就可以通过浏览器直接访问当前应用
http://localhost:2346默认使用 Workerman 工作方式,如果需要使用 Gateway 方式,需要安装 GatewayWorker 组件。
安装 GatewayWorker 的指令如下:
# 安装 GatewayWorker 组件
composer require workerman/gateway-worker注意: 启用 Gateway 时需要单独启动三个进程,分别是 Gateway、Register、Business,中间需要 Register 进程连接。
数据通信模型:
Client <-> Gateway <-> Register <-> Business <-> Events
Linux 支持操作指令如下:
php think xadmin:worker [start|stop|reload|restart|status|connections] [--daemon]
# 以上所有操作效果与 Workerman 官方操作一致,详情请阅读对应文档。Windows 支持操作指令如下:
php think xadmin:worker start --daemon
php think xadmin:worker [stop|status]
# Windows 仅允许 start、stop、status;其中 start --daemon 会通过 ThinkAdmin 进程服务创建后台进程。
# stop 与 status 基于系统进程查询实现,Workerman 官方不支持这种 Windows 管理方式。其他 Workerman 参数可以在应用配置目录下的 worker.php 里面 worker 项配置。其中 daemonize、statusFile、stdoutFile、pidFile、logFile 会写入 Workerman 静态属性,其余配置会写入当前 Worker 实例。
更多其他特性请阅读 workerman 文档 https://www.workerman.net/doc/workerman
版权说明
ThinkPlugsWorker 遵循 Apache2 开源协议发布,并提供免费使用。
本项目包含的第三方源码和二进制文件之版权信息另行标注。
版权所有 Copyright © 2014-2026 by ThinkAdmin (https://thinkadmin.top) All rights reserved。
