swoole的session共享不能直接用session_start(),因其常驻内存导致进程复用、$_session污染、文件存储冲突及生命周期钩子未接管;需实现sessionhandlerinterface接口并手动控制读写,禁用swoole\table,redis续期需主动刷新,session id须服务端安全生成。

为什么 Swoole 的 Session 共享不能直接用 session_start()
Swoole 是常驻内存的,PHP-FPM 每次请求都重启进程,而 Swoole Worker 进程会复用多次请求。这意味着:session_start() 默认基于文件存储、依赖进程隔离机制,在 Swoole 里会失效——多个请求共享同一个进程,$_SESSION 变量会被覆盖或污染,且不同 Worker 间完全不互通。
更关键的是,Swoole 不接管 PHP 的 session 生命周期钩子(如 php.ini 中的 session.save_handler),即使你配置了 Redis 存储路径,session_start() 仍会尝试写本地文件,报错类似:Failed to write session data (files)。
所以第一步必须绕过原生 session 机制,自己控制读写逻辑。
Redis 实现 Session 共享的核心是 SessionHandlerInterface
你需要实现这个接口的 6 个方法:open()、close()、read()、write()、destroy()、gc()。其中最关键的两个是:
-
read($id):从 Redis 读取session:$id的字符串值,返回反序列化后的数据(注意:Swoole 默认不启用serialize_handler=php_serialize,建议统一用json_encode/decode) -
write($id, $data):把$data序列化后写入 Redis,并调用$redis->expire("session:$id", $lifetime)刷新 TTL
注册时不能用 session_set_save_handler(new MySessionHandler()) 后再调用 session_start() —— 因为 Swoole 的协程上下文可能让 session ID 复用出错。推荐在每次 HTTP 请求入口手动调用 $handler->read($sessionId),并把结果挂到 request 对象上(如 $request->session),避免全局变量污染。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
别碰 Swoole\Table 存 Session
文档里偶尔提到用 Swoole\Table 做轻量 Session,但生产环境必须避开,原因很实在:
- 数据只存在内存,Worker 进程崩溃就全丢
- 没有持久化,无法跨机器共享(哪怕单机多 Worker,重启后也清空)
- 容量硬限制(最大约 1GB),且 key 冲突或超长会静默失败
- 并发写入无原子保障,
read + write非事务,高并发下可能覆盖
它适合做本地缓存或计数器,不适合 Session。如果你看到项目里用了,基本等于没做分布式容错准备。
Session 续期和自动清理要自己补全
Redis 的 EXPIRE 是被动淘汰,但用户活跃时必须主动续期,否则刚登录就掉线。常见错误是只在登录时设 TTL,后续请求不刷新:
- 在
read()方法末尾加$redis->expire("session:$id", $lifetime)是最稳妥的做法 - 不要依赖中间件统一续期——如果中间件没走到(比如静态资源请求),session 就会提前过期
-
gc()方法在 Swoole 里几乎没用,因为 PHP 的session.gc_probability不生效;应该用 Swoole 定时器(Swoole\Timer::tick())定期扫描KEYS session:*并清理过期项,或直接依赖 Redis 的 LRU + 过期策略
最后提醒一点:Session ID 本身必须由服务端生成(用 bin2hex(random_bytes(16))),不能前端传、不能 URL 拼接,否则绕过校验风险极高。这点比传统 PHP 更需手动把关。










