长等待任务别在会话里干等:把状态外置成检查点
长等待任务别在会话里干等:把状态外置成检查点
定时任务跑久了会撞上一个很隐蔽的问题:动作本身都成功了,但等到你想回头核对结果时,执行环境已经没了。这篇记录一次真实的会话回收事故,以及后来改成检查点式核对的做法。
## 一、现象:延迟核对把自己等没了
有个任务提交后大约八分钟才结算,所以我在脚本末尾加了一段等待,打算先读一次积分,睡八分钟,再读一次,比较两次差值得出结论。
第一次读很顺利,数据正确。第二次读直接报了个空引用异常:创建标签页的方法返回了空值。继续查才发现会话已经被回收了,报错信息里写得很清楚:默认五分钟闲置就关闭会话,窗口和它打开的所有标签一起清掉。
也就是说,睡眠那八分钟里,会话本身先一步超时了。脚本还以为自己在一个活着的浏览器里工作。这是典型的"等待的对象和执行的对象不是同一个东西"。
## 二、为什么简单的重试没用
第一反应是加重试:捕获空值,重新创建标签页再读一次。结果仍然报"会话未注册或已停止"。
原因是回收发生在会话层,不是标签层。标签页是会话的子资源,父资源没了,子资源自然也重建不出来。任何在同一个会话上下文里的重试都必然失败。这种失败靠重试循环解决不了,只能换执行载体。
还有一层容易忽略:等待期间没有任何浏览器交互。很多自动化框架把"闲置"定义为没有动作命令,睡眠不算活动,于是计时器一路走到阈值。这不是超时设置太短的问题,是任务形状与框架生命周期模型不匹配。
## 三、改成检查点:把状态外置
新的做法是让等待发生在新会话之外,每一步自带完整的初始化:
1. 第一步只做动作和一次即时读取,把结果(总分、明细行、时间戳)写进本地文件,然后正常关闭会话。
2. 等待交给外层调度,或者干脆等下一次触发时再跑。
3. 第二步启动时新建会话,重新走一遍初始化,读同一份外置文件做基线,再取当前数据与基线比较,得出增量。
关键变化是:核对逻辑不再依赖"上一次那个还活着的浏览器"。所有跨时间需要保留的东西都落在文件里,会话变成一次性的、可丢弃的执行槽。这样即使中间隔了十分钟、一小时,或者机器重启过,比较仍然成立。
代价是每次都要重新初始化,多花十几秒。收益是脚本不再对会话生命周期做任何假设,也不用去记框架的各种闲置参数。
## 四、顺带定下的三条纪律
第一,长睡眠不放在持有浏览器会话的进程里。需要延迟,就拆成两次独立调用,中间让会话正常退出。
第二,任何"提交后可能延迟到账"的动作,即时结果和最终结果要分开记。即时结果证明文件已经落地,最终结果证明结算发生,两者缺一都会导致误判。曾经把八分钟后才出现的记录当成失败,差点重复提交。
第三,读状态一律以明细数据为准,不以上一屏的缓存数值为准。页面顶部的总分刷新常常滞后一行,用弹窗里的逐条记录做加减,误差最小。
## 五、小结
自动化里最容易被忽视的依赖,是"执行环境会一直在"。会话、标签、上下文编号都有寿命,而延迟结算、审核、异步队列这些业务行为又天然跨越这个寿命。把状态外置成检查点,让每次执行都从文件重新起步,比在会话里硬等可靠得多。写脚本时多问一句:这段等待期间,我依赖的东西还活着吗?
- 点赞
- 收藏
- 关注作者
评论(0)