把我们的商品详情页变成 502 的浏览量计数器
配送
我们展示所有在售套件的页面在上线后约 41 小时内,对每位访客都返回 502 Bad Gateway。每个套件自己的页面正常,首页正常,结账正常。出问题的只是几行统计页面浏览量的代码。
这个 bug
我们商店的服务器统计套件页面的访问量。为了确定请求对应哪个套件,它取路径的第二部分:
self._count_view(path.strip("/").split("/")[1] if path.count("/") >= 2 else "")
对于 /kits/studio/,它是 studio。对于商品详情页本身 /kits/,路径有两个斜杠,所以防护机制让它通过了,但去掉斜杠后只剩一个部分,[1] 抛出了 IndexError。处理程序是 Python 的标准 http.server:do_GET 中的异常会关闭连接且不返回响应,Cloudflare 向访客显示 502。
修复方法是一个小函数,当部分缺失时返回空字符串。浏览量统计只需要套件名称;它从来不需要能够破坏它所统计的页面。
为什么我们的检查都没发现它
- 正常运行时间监控只打开首页。首页一切正常。
- 发布步骤检查了它发布的每个套件:其页面、其结账重定向、完整的付费路径。它从未打开所有套件的列表。
- 分析工具无法显示它。本应统计商品详情页访客的代码正是崩溃的代码。服务器日志一直都有答案:在这 41 小时内有 17 个 traceback,每一个都是一次请求,来自人或爬虫,却收到错误而不是页面。
发生了什么变化
- 每小时从外部检查每个页面。一个小任务读取 sitemap,像访客一样通过 Cloudflare 打开每个英文页面,并依次打开其他 42 种语言中的一个。连续两次运行失败会发送邮件;恢复后会发送另一封。
- 发布步骤在重建站点后打开每个英文页面,如果有一个页面未返回 200,则拒绝完成。
- 索引页也算作页面。列出其他页面的页面是访客最先到达的页面,也是没人测试过的页面。
要点
请求处理程序中的旁路工作(例如计数、日志记录或跟踪)绝不能导致请求失败,而仅覆盖你刚修改过的页面的页面检查会遗漏那些链接到这些页面的页面。Night Watch 中的看门狗接受任意页面列表,每个页面都指定必须包含的文本,而不仅仅是首页。