把商品目錄變成 502 的瀏覽次數計數器
運送
我們銷售所有套件的頁面,在產品上線後約 41 小時內,對每位訪客都回傳 502 Bad Gateway。每個套件自己的頁面正常運作,首頁正常運作,結帳流程也正常運作。出問題的是幾行計算頁面瀏覽次數的程式碼。
錯誤所在
我們商店的伺服器會計算套件頁面的造訪次數。為了找出請求對應的是哪個套件,它會取出路徑的第二個部分:
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 中的守護程序可接受任何頁面清單,每個頁面都指定必須包含的文字,而不僅限於首頁。