本文通过一个交易系统V16版因JS缓存导致的生产事故案例,分析了客户端缓存旧版脚本引发功能异常的原因。提出在JS引用链接后添加版本号参数(如?v=1)作为替代随机数的更优方案,通过递增版本号通知客户端更新文件,从而在保证缓存利用率的同时避免生产事故。
生产环境JS缓存引发的事故
近几个月来,团队专注于一个交易系统的持续改进项目,迭代发布周期约为2至3周。最近一次迭代为V16版,于周三完成发布。然而第二天上午,负责人便指出生产环境中出现了一个bug,具体问题如下:
系统中包含一个常用的自定义控件,用于协助选择客户。V16版的改进需求是为该控件增加两个筛选选项,以支持不同的默认值配置。这一需求实现简单,代码修改也不复杂,主要涉及在一个JS文件中给某个函数增加一个传入参数,用于传递配置值。经过RC(Release Candidate)和RTW(Ready To Work)测试,一切看似正常,但上线生产环境后却发现了问题:加载出的客户数据明显异常,数量不对,且与预期的查询配置不符。
检查控件内部的跳转链接后发现,传递的参数与预期不符,而该链接正是由上述修改过的JS函数生成的。因此判断,问题根源在于客户端缓存了旧版JS文件,导致新函数的调用被旧函数逻辑替代。清除缓存并重新加载页面后,该自定义控件恢复正常工作。遗憾的是,我们无法通过通知每位用户手动清除缓存来解决问题。
JS缓存控制与版本管理方案
此时意识到,必须引入一种机制来控制JS文件的缓存问题。否则,后续任何涉及JS文件内容的修改,都可能因客户端缓存无法获取最新文件而导致生产事故。
原则上,我们希望在JS更新时才重新加载文件,而非每次页面加载都强制刷新,因此直接在JS应用地址后添加随机参数的做法不可取,这会导致缓存失效,几乎每次加载都重新请求JS文件。但存在第二种更合理的做法:观察国外一些网站的代码会发现,他们通常在JS链接后添加版本号参数而非随机数。当JS代码发生修改时,只需将版本号加1,即可巧妙通知客户端更新JS文件。虽然不知此方法首创者是谁,但这无疑是一个值得钦佩的好主意。
附赠相关代码示例:<script src="../JavaScript/SelectOpenWindow.js?v=1" type="text/javascript"></script>