故障排查 從錯誤碼406說起
背景
前一段時間,我突然接到運營的同事通報,滬江的一位老師在國外登錄不上了滬江帳號。這本來是很普通的故障,但是在排查問題過程并不簡單,我們意外獲得了不少收獲,在這里與大家分享。
我們首先判斷,從故障現象來看,應該和后端無關,而是與前端有關,所以我們迅速查看了前端的日志,從日志來看,主要是用于判斷客戶端的地理位置接口持續出現錯誤,出現大量的HTTP Status Code 406(24小時之內出現了1w多條)。按照HTTP Status Code的規范,4開頭的錯誤碼和客戶端有關,考慮到這個故障只出現在一位老師那里,初步判斷406就是問題的根源。
隨著掌握信息的增加,分析的加深,我們迅速解決了那位外教的故障,不幸的是,確認它和406沒有關系。
但是,我們并不能就此打住。畢竟正常情況下響應的HTTP Status Code應該是200,那么大量的406到底是什么呢?為什么我們都無法復現?它們是如何引發的?如此大量的爆發應當引起用戶的反饋了?為什么線上的反饋這么平靜呢?
下圖為日志平臺中406錯誤的情況
排查過程
為了保障性能,我們的 Node 端并沒有詳細記錄每個請求,所以單純看406的日志并不能知道具體的原因。為了排查這個問題,我們緊急發布了在線補丁,具體記錄每個請求的詳細信息,然后在日志平臺中看到了下面的請求
為了便于對比,我們在瀏覽器上截取了正常的請求。如下圖
仔細對比這兩個請求,結合錯誤碼406的定義,我們的目光集中到了 Accept 這個header
日志中
而正常瀏覽器的行為
于是,我們在 Postman 中模擬了錯誤的請求,果然,我們復現了406錯誤,所以可以確認問題是 Accept 字段導致。
406 Not Acceptable 狀態碼表示客戶端錯誤,表示請求的資源的內容特性無法滿足請求頭中的條件,因而無法生成響應實體。 譯自HTTP協議規范RFC文檔
我們上網查閱資料并也跟后端同事討論了406的錯誤碼,得知,如果請求頭的 Accept 不符合事先約定的契約,就會返回406錯誤。報錯的是 API 服務,返回的是 application/json 格式的數據, 然而請求中的 Accept 說明它并不支持這種格式,所以會報出406錯誤。
我們仔細檢查了常見瀏覽器發送的請求,發現全部都包含 Accept: */* ;。看來,這些引發406的請求并不是普通用戶發出來的。那么,究竟是誰發出了這些請求呢?
難道是CDN?
CDN 的全稱是Content Delivery Network,即內容分發網絡。 其目的是使用戶可就近取得所需內容,解決Internet網絡擁擠的狀況,提高用戶訪問網站的響應速度。 CDN 網絡可以將服務器的內容緩存到分布全球的CDN節點,根據用戶的訪問 IP,就近連接 CDN,提高網站響應速度。(引用自google.com)
如今CDN已經是各種公司的普遍配置,滬江也不例外。我們仔細研究了引發406的請求來源IP,發現都是來自北京聯通的少數節點。這樣看來,CDN的嫌疑很大,大概有兩種可能:1、原始請求頭部的Accept 字段就是錯的;2、原始請求頭部的 Accept 字段是對的,但是在經過 CDN 節點的時候被 CDN 篡改了。由于以前遇到過 CDN 篡改頭部的問題,我們初步判斷是 CDN 的問題。
接下來,我們將北京聯通的節點暫時回源,驗證是不是 CDN 篡改了頭部,同時也拿到了最終的用戶 IP。 上網搜索這個IP詳細的信息,上面赫然寫著某搜索引擎的爬蟲。原來,406并不是來自于普通用戶,而是搜索引擎的爬蟲。
花絮
在寫文章的這幾天,發現錯誤日志下降了很多,406錯誤都沒有了。以為某某搜索引擎幡然悔悟,于是用當時出錯的 IP 去日志平臺搜索,發現該搜索引擎只是換了個策略。它的 Accept 字段做了修改,UA 頭中加上了該搜索引擎特有的標識,搖身一變又成了正規的搜索引擎。
小結
對開發人員來說,當站點遇到大量的406錯誤的時候,不用太擔心,好好查下日志,它很有可能是搜索引擎的爬蟲導致的。
總結下本次406錯誤碼事件,某搜索引擎在爬取滬江頁面的時候,請求頭設置 Accept 與后端服務所接受的 Accept 字段不同,從而導致大量的406錯誤。
***詳細講解下Header中 Accept 的相關知識
Accept
header中用它來告知客戶端可以處理的內容類型,這種內容類型用MIME類型來表示(引用自MDN)
內容類型
text/html,application/xhtml+xml,application/xml 都是 MIME 類型,也可以稱為媒體類型和內容類型。
示例中,application的是類型,json是子類型。它說明,客戶端只能夠接收application/json這種類型的響應。如果服務端不能返回這種類型的響應,服務端應當返回406錯誤。
通配符 * 代表任意類型
例如:Accept: / 代表瀏覽器可以處理所有類型
Accept可以支持用,分隔的多個類型
借助內容協商機制,服務器可以從諸多備選項中選擇一項進行應用,并使用 Content-Type 應答頭通知客戶端它的選擇。
它說明,客戶端能夠接收的響應類型只有三種:text/html,application/xhtml+xml,application/xml。
因子權重(q)
q是一個0-1之間的數值, q的默認值是1, q=0代表不可接受,q 值越大,請求越傾向于獲得其“;”之前的類型表示的內容
它說明,客戶端優先選擇text/html格式的響應,其次是application/xhtml+xml,***才是application/xml,*/*。