⚡ ThinkPlugsWorker

Latest Stable VersionTotal DownloadsMonthly DownloadsDaily DownloadsPHP VersionLicense

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() 按以下顺序初始化:

  1. 创建 ThinkApp,绑定 Worker 专用的 Request 和 Cookie 实现。
  2. 执行 RuntimeService::init($app)->initialize(),加载项目和插件运行环境。
  3. 首次创建 route 容器,加载启动阶段的基础路由和中间件。
  4. 调用 ThinkApp::captureWorkerState(),保存 global、app、route、controller 四类 Middleware 队列快照;多应用中间件也在此时建立启动基线。
  5. 从配置初始化 Workerman Session/Cookie 静态参数,其中 session.expire 会按整数使用。
  6. 注册数据库保活定时器,减少长时间空闲连接失效的影响。

启动阶段的 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_EXPIREint7200Session 过期时间,Worker 初始化时作为 Session lifetime 使用
CACHE_REDIS_PORTint6379Redis 连接端口
CACHE_REDIS_SELECTint0Redis 数据库编号

转换采用 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。

最近更新:
Contributors: 邹景立, Anyon