workerman 中 session_start() 必然失败,因其常驻多进程模型与 php-fpm 下 session 依赖的初始化、文件锁及 gc 机制根本冲突;必须弃用 $_session,改用 redis 手动管理 session id 与数据,并通过 cookie 传递、显式设置 ttl。

Workerman 里调用 session_start() 一定会报错,不是你代码写错了,是它根本不能用。 因为 Workerman 是常驻内存的多进程模型,而 session_start() 依赖 PHP-FPM 每次请求重启时的初始化机制和共享文件锁,两者底层逻辑冲突。硬要调用,只会触发 “Cannot start session” 或 “Failed to initialize storage module” 这类错误。
为什么 session_start() 在 Workerman 中必然失败
这不是配置问题,也不是路径没写对 —— 是模型级不兼容:
- Workerman 的每个 worker 进程长期运行,
session_start()调用后无法自动清理或同步 session 文件,多个进程同时读写同一 session ID 的文件会直接覆盖或锁死 - PHP 原生 session 默认走
fileshandler,但 Workerman 不保证所有 worker 进程有相同session.save_path权限,也无自动创建目录逻辑 - 即使你手动
ini_set('session.save_path', '/tmp'),也解决不了跨进程数据可见性问题:A 进程 set 的 $_SESSION,B 进程完全读不到 - HTTP 请求和 WebSocket 连接可能被不同 worker 处理,而原生 session 没有跨 worker 通信机制
正确替代方案:用 Redis 手动管理 session ID 和数据
绕过 PHP 原生 session,自己控制生命周期。核心就三步:生成 ID → 存 Redis → 每次请求取出来用。
- 不要调用
session_start(),彻底删掉或注释掉所有相关代码 - 从 Cookie 或 Header 里手动提取 session ID(比如读
$_COOKIE['PHPSESSID'],或你自定义的X-Session-ID) - 用这个 ID 作为 Redis key,
GET/SETJSON 格式的用户数据(如{"user_id": 123, "login_time": 1716512000}) - 登录成功后,生成新 ID(推荐
bin2hex(random_bytes(16))),存入 Redis 并写回 Cookie;退出时DEL对应 key
示例片段(非完整逻辑):
// 登录成功后
$sessionId = bin2hex(random_bytes(16));
$redis->setex("session:$sessionId", 3600, json_encode(['user_id' => $uid]));
setcookie('PHPSESSID', $sessionId, ['expires' => time() + 3600, 'httponly' => true, 'secure' => true]);
连接级状态用 $connection->session,别混用
如果你只是想在单个 WebSocket 连接里临时存点东西(比如用户角色、房间号),不需要跨连接共享,那可以直接挂到连接对象上:
-
$connection->session = ['user_id' => 123];完全合法,且高效 - 但它只存在于当前连接所在的 worker 进程内存中,断连即销毁,换一个连接或另一个 worker 就不可见
- 千万别把它和 Redis 存的全局 session 数据混淆使用,否则会出现“同一个用户两个标签页互相踢下线”这类问题
- 每次访问前必须判空:
if (isset($connection->session['user_id'])) { ... },因为新连接时该属性为null
最易被忽略的一点:Redis 的 key 过期时间必须显式设置(如 setex),不能依赖 PHP 的 session.gc_maxlifetime —— 那个值在 Workerman 里根本不生效。











