根本原因是session未及时保存且重连后使用新实例;应改用redis实现uid绑定的跨进程共享session,并通过gatewayworker的binduid与businessworker的redis读写协同恢复状态。

Workerman + GatewayWorker 架构下,断线后 session 数据丢失,根本原因不是“session 本身被删了”,而是 连接断开时 session 没来得及保存,且后续重连建立的是新连接、新 session 实例。GatewayWorker 作为纯转发层不处理业务逻辑,也不存储 session;真正做 session 管理的通常是后端 BusinessWorker(或 HTTP Worker),而 session 默认文件存储又无法跨进程共享——这就导致:用户重连后虽然还是同一个客户端 IP 或 UID,但 session 数据却是空的。
Session 数据必须脱离连接生命周期
session 的本质是“用户状态映射”,不是“连接状态映射”。GatewayWorker 负责连接管理与消息路由,BusinessWorker 负责业务处理,两者分离意味着 session 必须独立于 TCP 连接存在。只要用户身份(如 uid、token、设备 ID)可识别,就该能恢复其 session 数据。
- 不要依赖
$request->session()在 GatewayWorker 中操作 session —— 它只在 HTTP Worker 场景下有效,GatewayWorker 是长连接,没有 Request 对象 - BusinessWorker 中的 session 默认存磁盘文件,每个进程一份,不同进程间不共享,重连后可能落到另一个 BusinessWorker 进程,自然读不到原 session
- 断线期间若没主动调用
$session->save(),PHP 销毁对象时才写入,而连接中断可能导致销毁流程未执行完
用 Redis 实现跨进程 session 持久化
这是最直接、最稳定的解法。Redis 作为中心化存储,所有 BusinessWorker 进程共用同一份 session 数据,UID 绑定后即可按需读写,不受连接断开/重连影响。
- 在
onWorkerStart中初始化全局 Redis 客户端(非静态变量,避免跨进程污染):$GLOBALS['redis'] = new Predis\Client(['scheme' => 'tcp', 'host' => '127.0.0.1', 'port' => 6379]); - 封装统一的 session 存取方法,例如:
function session_set($uid, $key, $value) { $GLOBALS['redis']->hSet("session:{$uid}", $key, json_encode($value)); }<br>function session_get($uid, $key, $default = null) { $val = $GLOBALS['redis']->hGet("session:{$uid}", $key); return $val ? json_decode($val, true) : $default; } - 用户登录或绑定 UID 后,立刻用 UID 作为 session 主键,后续所有业务逻辑都基于该 UID 读写,不再依赖 PHP 原生 session 生命周期
配合 GatewayWorker 的 UID 绑定与重连识别
GatewayWorker 提供了 $connection->bindUid($uid) 和 gateway->isOnline($uid) 等能力,这是实现“断线不丢态”的关键桥梁。
- 首次连接完成认证后,立即调用
bindUid,确保该用户所有后续连接(包括重连)都能被识别为同一 UID - BusinessWorker 收到消息时,先通过
GatewayClient::getUidByConnectionId($connectionId)获取 UID,再从 Redis 加载对应 session 数据 - 客户端重连成功后,可主动发送一个
{"cmd":"sync_session"}消息,BusinessWorker 收到后把 Redis 中最新 session 数据推回客户端,实现状态同步
补充:避免因断线导致 session 覆盖或清空
有些项目会把 session 写在客户端本地(如 localStorage),再每次发给服务端合并——这不可靠,易被篡改且无法保证一致性。更稳妥的做法是:
- 所有敏感状态(如权限、临时 token、采集配置)只存在服务端 Redis 中,客户端仅持有一个短期有效的 access_token
- access_token 关联 UID,在每次请求中校验并刷新过期时间,避免 token 失效导致 session “凭空消失”
- 对关键操作(如设备控制指令)增加 session 版本号(version 字段),防止旧连接残留消息覆盖新 session 状态











