1. 一个典型的线上事故:跨场景事件持久化失效的全过程
1.1 事故现场
我至今还记得那个周三下午。监控大屏上突然亮起一片红点,支付成功率在十分钟内从正常的 93% 掉到了 61%,紧接着客服群里开始有人反馈“订单提交失败”“一直转圈”。我们当时的第一反应是支付网关出问题了,因为从用户侧看,问题几乎都集中在“最后一步点击支付”这个动作上。
但奇怪的是,排查网关日志和支付回调时,一切正常——支付渠道侧没有任何异常告警,回调也都正常送达了。真正的问题发生在我们的前置环节:用户在确认页点“提交订单”的瞬间,服务端返回了一串奇怪的错误码,指向一个早就该存在的“临时订单上下文”在服务端找不到了。
这不是偶发故障,而是所有人都在踩、但很少有人系统聊过的坑——跨场景事件的状态持久化。
1.2 从表象到根因的排查链路
我们花了一个多小时才把链路完整串起来。
用户视角的流程是这样的:从商品详情页加入购物车 → 购物车页点结算 → 确认订单页(选择地址、优惠券)→ 提交订单 → 收银台页面 → 支付成功。
前三段在前端是没有问题的,问题出在“购物车页点结算”到“确认订单页”之间。我们在结算瞬间会把用户勾选的商品快照、默认地址、可选优惠券等信息打包成一个“临时订单上下文”,传给后端接口,后端把它缓存在 Redis 里,用的是购物车会话 ID 做 key,业务上预期是这些数据够用 15 分钟,足够用户在确认页完成地址修改和优惠券选择。
那天出事的触发条件是:我们上线了一版新的服务节点灰度,负载均衡策略从“IP 哈希”改成了“轮询”。原来同一个用户在整个下单链路里大概率会打到同一台机器,所以上下文数据虽然在 Redis,但业务代码里有一段逻辑还会在本地缓存里再放一份。改成轮询之后,第一个请求打到了 A 节点,第二个请求打到了 B 节点,B 节点查不到本地缓存,回源查 Redis 理论上也能查到,但偏偏那版代码回源时的 key 组装多拼了一个环境标识,导致新老节点查的不是同一个 key。
于是,用户从购物车跳转到确认页时,Redis 返回空,代码里没有做兜底重建逻辑,直接抛异常。用户看到的就是“提交订单失败”。
这个问题的本质,就是事件在跨场景流转时,依赖了不该依赖的“短时内存态”,而且没有做好持久化和恢复。更扎心的是,这个事故代码在测试环境怎么测都测不出来,因为测试时永远只有一台节点在跑。
1.3 为什么几乎没人聊这个问题
事后复盘我们发现,这个问题在行业内之所以“没人聊”,主要有三个原因。
第一,它很难在技术分享里成为一个“有亮点”的话题。发版、灰度、负载均衡这些大家都爱聊,但“临时订单上下文在 Redis 里放多久”“怎么设计快照让用户在场景切换后还能无缝继续”这类问题,看起来不像能做出一篇漂亮 PPT 的题材,多数团队把那次事故归因成“实习生改了负载均衡策略”,就结束了。
第二,它往往藏在业务驱动开发的盲区里。产品经理关注的是一条路径能不能走通,极少有人会问“用户在第 3 步走到一半杀掉 App,5 分钟之后回来应该看到什么”。但这个问题的答案,恰恰就是跨场景事件持久化的核心需求。
第三,它只有在规模到了一定程度才会集中暴露。单机部署、单线程处理的时候,内存变量到处传也无所谓。一旦开始横向扩展、微服务拆分、多端并行,所有状态共享问题就会像火山一样喷发。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 跨场景事件的三种典型形态与各自的持久化难点
跨场景事件不是一个教科书词汇,我理解它的定义是:一个开始于场景 A、结束于场景 C 的完整业务动作,在 B、C 等中间场景流转时,需要共享同一份状态数据。根据我接触过的项目,它大致可以分为三种形态,持久化的难度也各不相同。
2.1 形态一:UI 状态跨页面/跨端迁移
最常见也最容易被轻视的一种。用户在 Web 端做一个多步表单,填到第三页刷新了浏览器,填好的内容全没了;或者用户在手机 App 上挑好了商品,切到电脑上准备继续结算,购物车还在,但结算时的个性化选项全丢了。
这类问题的持久化难点在于:UI 状态通常是无结构的、碎片化的,前端组件的内部状态、滚动位置、临时勾选,全部混在一起。你很难定义“哪一部分状态值得持久化”,所以很多团队直接用浏览器的 localStorage 或者 sessionStorage 一把梭,把所有状态都塞进去。
但 localStorage 有两个致命的天花板:第一是容量限制,5MB 看着很多,一旦放几张 base64 的图片就扛不住了;第二是隔离性,它只对当前源生效,用户从 Web 端切到 iOS App 时,Web 上存的那份状态根本过不去。
2.2 形态二:业务事件跨模块/跨服务流转
这是最接近“事件驱动架构”的一种形态。订单创建后,库存服务要扣减库存,支付服务要发起支付,通知服务要发短信,积分服务要加积分。这些服务通过消息队列传递事件,每个服务都
