workerman 处理超大 http 请求时易因内存超限崩溃,需在连接层、workerman 层、php 层和应用层四重防护:设缓冲区限制、校验请求头/体大小、流式处理上传、强制分片上传。

Workerman 处理 HTTP 请求时,对超大请求包(如超长 URL、巨量 POST 数据、超大文件上传)缺乏默认保护机制,容易因一次性读取全部原始数据而触发 PHP 内存限制(memory_limit),导致进程崩溃或响应超时。这不是 Workerman 的设计缺陷,而是其底层基于原始 socket 的轻量特性决定的——它不内置 HTTP 协议解析器,也不自动流式截断或拒绝非法大包。
HTTP 请求头过大直接触发内存溢出
Workerman 的 HttpWorker 在接收到完整请求头后才开始解析。若客户端发送超长 Cookie、User-Agent 或自定义 header(例如拼接 10MB 的 token 字符串),Workerman 会将整段 raw header 缓存在连接对象的 $connection->input 中,直到解析完成。此时 PHP 内存已占用数 MB,可能瞬间突破 memory_limit。
- 典型表现:PHP 报错
Fatal error: Allowed memory size of XXX bytes exhausted,且错误发生在Workerman/Protocols/Http.php的parseRequest()过程中 - 规避方式:在
onMessage回调开头,检查$request->rawHeaders()总长度或单个 header 长度,超过阈值(如 8KB)立即$connection->close() - 不要依赖
$_SERVER['HTTP_*']——它们是解析后生成的,出错时根本到不了这一步
超大 POST 或文件上传未流式处理
Workerman 默认将整个 HTTP body 加载进内存再交由 $_POST 和 $_FILES 解析。当上传 500MB 文件且未启用 upload_tmp_dir 或磁盘空间不足时,file_get_contents('php://input') 会直接失败;即使成功,也会双倍占用内存(原始流 + $_POST 数组)。
- 正确做法:禁用自动解析,改用
php://input流式读取 + 分块写入磁盘,避免全量加载 - 设置
ini_set('memory_limit', '512M')是治标不治本;应配合stream_copy_to_stream()和临时文件句柄控制实际内存驻留 - 对
multipart/form-data,必须手动解析 boundary,不能依赖$_FILES—— 它内部会把整个 body 拷贝进内存再拆分
连接层提前拦截,防未解析请求耗尽资源
Workerman 允许你在协议解析前就干预原始数据流。这是防御超大请求最有效的层级。
- 监听
onBufferFull:为每个 HTTP 连接设置$connection->send_buffer_limit = 1024 * 1024(1MB),并在回调中主动断连,防止恶意填充 - 重写
onMessage前置校验:读取$connection->getInput()前先检查当前输入缓冲区大小(strlen($connection->input)),超过 2MB 立即 close - 配合
maxConnections和系统级ulimit -n:单个大请求不可怕,但 1000 个并发大请求会快速耗尽 fd 和内存,需同步收紧系统连接上限
推荐的生产级防护组合
仅靠某一项配置无法兜底。真实场景需多层设防:
- 系统层:设置
ulimit -n 65535,并配Worker::$maxConnections = 50000(留 15% 余量) - Workerman 层:在
onConnect中初始化连接级限流,如$connection->max_input_size = 8 * 1024 * 1024(8MB),并在onMessage开头校验 - PHP 层:禁用
post_max_size和upload_max_filesize的自动限制(Workerman 不走 PHP-FPM SAPI),改由代码逻辑控制 - 应用层:对上传接口强制要求分片上传,服务端只接受
Range和Content-Range,彻底规避单次大包











