phpEnv下必须源码编译安装Swoole,因pecl默认调用系统PATH中首个php的php-config,导致扩展装错版本;Nginx代理需显式透传X-Forwarded-For和Upgrade头,否则WebSocket失败且真实IP丢失。

phpEnv 环境下装 Swoole 扩展,不能直接用 pecl install,必须源码编译;Nginx 代理必须显式传递 X-Forwarded-For 和 Upgrade 头,否则 WebSocket 和真实 IP 会失效。
phpEnv 中无法用 pecl install swoole 的原因
phpEnv 是多版本 PHP 共存管理工具,它把每个 PHP 版本的 bin、lib、etc 隔离在独立目录(如 ~/.phpenv/versions/8.1.22),而 pecl 默认调用的是系统 PATH 中第一个 php 对应的 php-config,大概率不是你当前激活的版本。
常见错误现象:
-
pecl install swoole成功,但php -m | grep swoole没输出 -
php --ri swoole报错Extension 'swoole' not present - 运行时提示
Class 'Swoole\Http\Server' not found
根本原因是扩展被装到了错误 PHP 版本的 extension_dir 下,或 php.ini 加载路径不对。
立即学习“PHP免费学习笔记(深入)”;
实操建议:
- 先确认当前生效的 PHP 版本和配置路径:
which php+php --ini - 进入对应版本目录,手动指定
php-config路径:./configure --with-php-config=/home/yourname/.phpenv/versions/8.1.22/bin/php-config - 编译前确保已安装
autoconf、gcc、make和php-dev(Ubuntu)或php-devel(CentOS)
编译安装 Swoole 时必须指定的 configure 参数
phpEnv 下的 PHP 通常不带 OpenSSL、HTTP/2 或 MySQLnd 支持,若项目需要 HTTPS 客户端、WebSocket over TLS 或异步 MySQL,则必须显式启用对应模块,否则运行时报错或功能缺失。
典型安全/功能组合参数(以 PHP 8.1 为例):
-
--enable-openssl:启用 TLS 支持,否则Swoole\Http\Client无法访问 HTTPS -
--enable-http2:启用 HTTP/2 协议栈,否则$server->set(['http_compression' => true])不生效 -
--enable-sockets:启用 socket 扩展依赖,否则Swoole\Coroutine\Socket初始化失败 -
--enable-mysqlnd:启用 MySQL native driver,否则Swoole\Coroutine\MySQL报mysqlnd is not enabled - 避免漏掉
--with-openssl-dir(Linux)或--with-openssl-dir=/usr/local/opt/openssl@3(macOS Homebrew)
编译命令示例:
cd swoole-src-5.0.3
~/.phpenv/versions/8.1.22/bin/phpize
./configure --with-php-config=~/.phpenv/versions/8.1.22/bin/php-config \
--enable-openssl \
--enable-http2 \
--enable-sockets \
--enable-mysqlnd \
--with-openssl-dir=/usr
make && make install
完成后检查 extension_dir 输出路径是否属于当前 PHP 版本,再编辑对应 php.ini,追加:extension=swoole.so。
Nginx 反向代理到 Swoole 必须配齐的 header 和超时项
只写 proxy_pass http://127.0.0.1:9501 是不够的。Swoole HTTP Server 依赖 Nginx 透传关键请求上下文,否则会出现:
- 所有请求的
$request->server['remote_addr']都是127.0.0.1 - WebSocket 连接立即断开,日志报
client sent invalid upgrade header - 长连接被 Nginx 强制关闭,
Connection: keep-alive失效 - POST 数据体为空,
$request->post始终为[]
最小可用反向代理配置(location 块内):
proxy_pass http://127.0.0.1:9501; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 360; proxy_send_timeout 360;
注意点:
-
proxy_http_version 1.1是Upgrade头生效的前提 -
proxy_read_timeout必须大于 Swoole 的request_timeout(默认 30s),否则 Nginx 先断连 - 不要加
proxy_buffering off,Swoole 自身已做流式响应,关了反而降低吞吐 - 静态资源(js/css/img)建议由 Nginx 直接
root服务,不走 proxy_pass
验证 Swoole 是否真正加载并被 Nginx 正确代理
光看 php -m | grep swoole 或 phpinfo() 显示 loaded 不代表能用。必须验证三层链路:PHP → Swoole Server → Nginx → 外网请求。
快速验证步骤:
- 写一个最简
server.php:<?php $http = new Swoole\Http\Server('0.0.0.0', 9501); $http->on('Request', function ($req, $resp) { $resp->header('Content-Type', 'text/plain'); $resp->end("OK\nIP: {$req->server['remote_addr']}"); }); $http->start(); - 后台启动:
nohup php server.php > /dev/null 2>&1 & - 本地 curl 测试直连:
curl http://127.0.0.1:9501→ 应返回 OK 和真实 IP(非 127.0.0.1) - 再通过 Nginx 域名访问:
curl -H "Host: your.dev" http://127.0.0.1→ 输出 IP 应与上一步一致 - 检查 Nginx error log 是否有
upstream timed out或connection refused
最容易被忽略的是:Swoole 启动后监听的是 0.0.0.0:9501,但防火墙或 Docker network 可能拦截该端口;phpEnv 用户常忘记 php --ini 显示的是 CLI 配置,而 FPM 或 Web 运行用的是另一个 php.ini —— 二者 extension 加载必须一致。



















