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