根本原因是网关多个实例未共享Redis客户端实例,导致连接隔离、pipeline缓存独立、健康状态不一致;必须全局复用单个redis.Client实例,禁用handler内new client,Session ID需服务端生成并闭环校验。

Go网关多个实例读不到刚写入的Session,根本不是Redis慢
根本原因是每个 Go 实例都 new redis.Client() 了独立客户端,哪怕连的是同一个 Redis 地址,连接池、pipeline 缓存、健康检查状态也完全隔离。A 实例执行 SET session:abc {"uid":123} EX 1800 NX 返回 OK,B 实例立刻 GET session:abc 却返回空——这不是数据没同步到 Redis,而是 B 的 client 根本没“感知”这次写入(Redis 本身不广播跨 client 的变更)。
- 必须在应用启动时全局初始化**唯一一个**
redis.Client实例,所有 handler 共享它(例如通过包级变量globalsessionkeeper.GlobalRedisClient暴露) - 绝对禁止在 HTTP handler 或中间件里调用
redis.NewClient(),否则连接数指数增长、连接复用失效、TIME_WAIT 爆满 - 若用
redis.ClusterClient,别直接调Do(ctx, cmd);改用Get(ctx, key)、Set(ctx, key, val, ttl)等封装方法,它们内部会自动重试并定位哈希槽
Docker Compose 下连不上 Redis,90% 是网络配置写死了
本地开发时常见现象:Go 服务跑在宿主机,Redis 容器名是 redis,但代码里硬编码了 localhost:6379,结果连不上——因为宿主机的 localhost 指向自己,不是容器网络。
- Docker Desktop 用户:把 Redis 地址改成
host.docker.internal:6379 - Linux Docker 用户:用宿主机真实局域网 IP(如
192.168.1.100:6379),不能用127.0.0.1 - 更健壮的做法:通过环境变量注入地址,如
REDIS_ADDR=redis:6379,并在 Docker Compose 的network_mode: "bridge"下确保 service 名可解析
Session ID 生成和校验不一致,导致“登录成功却跳回登录页”
这不是 JWT 签名校验失败,而是协议层(Cookie 传递)和服务层(Redis Key 存储)脱节。典型表现是前端收到 Set-Cookie: session_id=xxx,但后续请求压根没带这个 Cookie,或服务端解析出的 session_id 和 Redis 中存储的 key 对不上。
- Session ID 必须由服务端用
crypto/rand.Read()生成,严禁接受客户端传入的任意字符串(防碰撞 + 防伪造) - 设置 Cookie 时,
Domain字段要显式设为当前主域名(如.example.com),否则子域名(api.example.com)无法读取主站设置的 Cookie - 网关启用 HTTPS 但测试环境走 HTTP 时,务必关闭 Cookie 的
Secure属性,否则浏览器拒绝发送该 Cookie - 校验逻辑里别直接比对
r.Header.Get("Cookie")字符串——要用r.Cookie("session_id"),它自动处理分号分隔、URL 解码和匹配逻辑
并发登录覆盖旧 Session,WebSocket 连接突然断开
用户快速点两次登录按钮,后端两个并发请求都生成新 Session 并写入 Redis,旧 Session 被覆盖,但已建立的 WebSocket 连接仍持有旧 session_id,后续心跳或消息校验失败,连接被强制关闭。
- 写入 Session 时必须加幂等控制:用
SET session:xxx value EX 1800 NX,NX参数保证同一 key 只能成功写入一次 - 过期时间
EX值建议略大于最大预期登录耗时(如 300 秒),但绝不设为永不过期——故障时脏数据永远残留 - 不要锁整个用户(如
LOCK user:123),那是过度设计;锁的是“这次登录动作”,key 应基于登录凭证(如手机号+IP哈希)构造,而非用户ID本身
关键点在于:Redis 客户端实例是否全局复用,决定了 Session 是否真“分布式”;而 Session ID 的生成、传输、校验三环节是否闭环,决定了用户态是否稳定。这两处任一失守,都会让“分布式 Session”退化成“多个独立 Session”。


















