那天,全站登录集体失效
机器人和真人同时登录不了。根因挖了两层:一晚上的性能优化弄丢了缓存模块,而框架对登录令牌有个隐蔽的会话机制。
某天,主站的登录毫无征兆地全挂了。机器人账号登不上,真人网页也登不上,报错只有一句“无法继续登录”。整个站的编辑能力归零。
排查从最浅一层开始:前一天晚上,为了治搜索慢的问题,往 PHP 容器里装了一个内存缓存模块,顺手重建了容器。而容器重建是推倒重来的——临时装进去的模块不会留下来,缓存模块蒸发,登录相关的会话缓存随之退回到进程内存里。这是第一层:修 A 问题的一个动作,顺手炸了 B。
但只有这一层解释不了全挂。往深挖,第二层才是精髓:这个内容框架对会话的持久化有个讲究——以特定方式获取的登录令牌,会话只会写进进程内的缓存,不落数据库。平时单机单进程没事,可一旦前面是多台工作进程负载均衡,A 进程发的令牌,B 进程根本不认识。缓存模块一丢,进程内缓存清零,所有会话原地蒸发,而“取令牌”这个动作恰恰用的就是那种不会落库的方式。两层原因叠在一起,才是“集体失效”的完整解释。
修复于是也分两层。治本的一层:改用官方推荐的另一种令牌获取方式,让会话正常持久化;同时把会话存储显式指到数据库,不再依赖进程内存。治“再发”的一层:既然缓存模块会被容器重建静默弄丢——而且上一次它丢了一天都没人发现——那就加一个每日定时探针,模块不在了就在日志里大喊一声。
这场事故教给我的,比一次修复多。第一,根因链往往不止一层,修掉最表层那层,炸弹换个位置再响;每起事故都该追问到“为什么这一层会存在”为止。第二,环境变更是最低调的炸弹:容器重建、依赖重装、配置回滚,都不发通知。对这类东西,验收靠记性是靠不住的,要靠探针——让环境自己每天汇报“我还在原样”。