2026-03 需求03线上故障复盘与发布经验(Redis误切只读导致50001)
一、事件概述
事件类型:线上可用性故障(认证签发链路失败)
影响接口:
POST /api/v1/auth/exchange
表象错误:后端返回
50001 issue_token_failed,随后业务接口连带出现40105
核心根因:Redis 运行时被切换为从库且只读,认证签发流程写会话失败
处理状态:已止血、已修复代码可观测性、已完成热修复包
二、现象与影响范围
1)业务现象
客户端登录交换令牌失败:
/api/v1/auth/exchange返回50001
由于未拿到有效 access token,后续受保护接口(如
GET /api/v1/couple/relation、POST /api/v1/couple/invites)出现40105
客户端即使传了
huawei_credential,仍会失败(因为失败点在凭证校验之后)
2)影响范围
所有走
auth/exchange的用户均受影响
功能上表现为“无法登录/无法获取新会话”
三、关键证据链(本次定位的决定性信息)
1)应用访问日志
同一来源 IP 多次触发:
POST /api/v1/auth/exchange->error_code=50001
同时段紧随其后出现
40105(令牌缺失或失效导致)
2)Redis 复制状态(容器内)
role:slave
master_host:47.236.128.72
master_port:60149
slave_read_only:13)Redis 配置查询
CONFIG GET replica-read-only -> yes
CONFIG GET replicaof -> 47.236.128.72 601494)Redis 运行日志
大量出现:
MASTER <-> REPLICA sync started
Timeout connecting to the MASTER...
Reconnecting to MASTER 47.236.128.72:60149 after failure
结论:Redis 已被配置为副本并持续尝试连接外部 master,处于只读态,写入会话必然失败。
四、根因分析(Why)
1)直接根因
认证签发流程中,后端会先写 Redis 会话再签发 token:
生成
session_id+refresh_token原始值
写入 Redis 会话键:
auth:session:{session_id}
再签发 access token
当 Redis 处于只读副本时,第 2 步失败,从而返回 50001(历史版本日志中被笼统归类为 issue_token_failed)。
2)深层原因
Redis 运行时存在
replicaof指向外部地址47.236.128.72:60149
导致实例切为
role:slave且replica-read-only=yes
由于服务日志粒度不足(仅输出笼统 reason),定位耗时增加
3)为什么“之前正常,后来突然异常”
这类问题通常由运行期命令/外部操作触发,而非应用代码逻辑变更本身
若配置文件无
replicaof,而运行态出现replicaof,可判定为运行时被改写(误操作/脚本/外部访问)
五、现场处置(How)
1)止血动作
docker exec -it <redis_container> redis-cli REPLICAOF NO ONE2)止血验证
docker exec -it <redis_container> redis-cli INFO replication
# 期望:role:master
docker exec -it <redis_container> redis-cli SET __auth_probe 1 EX 60
# 期望:OK3)恢复后状态
Redis 回到
role:master
写入探针成功
认证链路恢复可用
六、本次代码修复(已完成)
1)错误可观测性增强(auth/exchange)
旧行为:统一
50001 issue_token_failed,难定位新行为
Redis 会话存储不可用 ->
50002,消息包含session store unavailable: <raw_error>
其他签发异常 ->
50001,消息包含issue token failed: <raw_error>
审计日志新增
raw_error字段,保留系统原始错误
2)生产配置安全校验增强
新增规则:
prod环境下redis.password不能为空,否则启动失败
3)生产配置更新
prodRedis 密码已改为目标值(后续建议再次轮换并走密钥管理)
七、发布产物与校验信息
发布包:
backend/dist/lover-api-linux-amd64-20260309-hotfix.tar.gz
SHA256:
fca8b75e0cf44d7727f686e67b07a75746d23d62c444c9219fcae5219c2b50d4
包内包含:
lover-api
configs/dev.yaml
configs/prod.yaml
migrations/0001~0005
八、systemd 配置经验(本次踩坑重点)
1)现象
已写入
Environment="REDIS_PASSWORD=...",但进程里仍为空
2)根因
同名环境变量在多个 drop-in 中重复定义,后者覆盖前者
实际出现:一处为
REDIS_PASSWORD=password_redis@!#另一处为REDIS_PASSWORD=
最终进程环境为
REDIS_PASSWORD=
3)规则
同名变量只保留一处定义,避免冲突
用
/proc/<PID>/environ校验运行时结果,不依赖status页面展示
九、防复发清单(必须执行)
A. Redis 侧
配置强密码/ACL
禁止公网直连 6379(仅白名单内网访问)
redis.conf移除replicaof/slaveof(若非主从架构)
考虑禁用危险命令:
CONFIG、REPLICAOF、SLAVEOF(按运维策略)
重启后复检:
INFO replication为role:masterCONFIG GET replicaof为空
B. 应用侧
认证失败必须携带可定位错误(禁止笼统 reason)
增加告警:
/auth/exchange的50001/50002异常率
增加 Redis 可写性巡检(探针写入 + 告警)
C. 发布侧
发布前校验二进制格式(ELF)
发布后固定冒烟:
health、auth/exchange、关键业务链路
记录
request_id对应审计与系统日志,形成可追踪证据
十、建议的长期优化
将 Redis/PG/Auth 等密钥迁移到专用密钥管理,不在会话中明文传递
在应用启动阶段增加“Redis 可写性自检”,失败则拒绝启动
将认证错误码进一步标准化:明确区分依赖故障、签名故障、输入故障
将此次复盘纳入发布 checklist 和 on-call SOP