業(yè)務(wù)前端界面報(bào)錯(cuò)504排查思路和解決辦法
1.背景
本文主要是寫的最近比較影響深刻的一次排查客戶訪問業(yè)務(wù)前端域名,報(bào)504,timeout錯(cuò)誤問題的記錄,該客戶為私有化部署,給客戶部署的服務(wù)存在跨洲調(diào)用,沒有專線,澳洲調(diào)用歐洲的服務(wù)情況,可能存在網(wǎng)絡(luò)延遲比較大,需要排查504的具體原因,然后通過優(yōu)化參數(shù)臨時(shí)解決。
2.排查步驟和思路
2.1 故障現(xiàn)象溝通
對(duì)于toB的客戶來說,通常在使用我們產(chǎn)品的時(shí)候,報(bào)錯(cuò)只會(huì)反饋一個(gè)截圖,我們需要向客戶溝通或者關(guān)鍵的信息,有利于問題排查。
比如:
- 打開的什么頁面,便于自己復(fù)現(xiàn)
- 具體報(bào)錯(cuò)的接口是哪個(gè)?
- 大概的報(bào)錯(cuò)時(shí)間
- 如果有x-request-id,拿到請(qǐng)求id
- 具體報(bào)錯(cuò)的url
2.2 梳理整個(gè)訪問請(qǐng)求的鏈路
我們需要了解,瀏覽器上的請(qǐng)求鏈路,才能更好的去排查問題,比如我遇到的這個(gè)問題,請(qǐng)求鏈路是這樣的。
客戶機(jī)器訪問瀏覽器域名 -> 私有端域名cdn(1) -> 私有端 SLB(2) -> 私有端 nginx(3)-> saas端服務(wù)域名cdn (4) -> saas端 SLB (5) -> saas 端nginx(6) -> saas端業(yè)務(wù)后端服務(wù)。
每個(gè)公司的業(yè)務(wù)情況不一樣,根據(jù)自己的實(shí)際情況梳理。
2.3 查看日志
- 第一次問題排查
通過第一步故障現(xiàn)象的溝通,獲取的內(nèi)容,然后去看鏈路上nginx(3),即私有端nginx的日志,想確認(rèn)請(qǐng)求是否到達(dá)了服務(wù)器,根據(jù) x-request-id搜索到日志,時(shí)間點(diǎn)和path也能對(duì)上,狀態(tài)碼是504,請(qǐng)求時(shí)間是30s,頁面多次刷新都是30s超時(shí)。
于是檢查nginx上的配置,發(fā)現(xiàn)該接口location里面的后端服務(wù)器響應(yīng)時(shí)間,proxy_read_timeout時(shí)間設(shè)置為30s,相當(dāng)于nginx會(huì)等待30s的時(shí)間來獲得請(qǐng)求的響應(yīng),如果在30s內(nèi)如果響應(yīng)接收不完,就會(huì)報(bào)出來504 timeout。
于是,修改了將進(jìn)行proxy_read_timeout時(shí)間修改為了300s,然后reload一下nginx。
- 第二次問題排查
客戶反饋訪問頁面依賴報(bào)錯(cuò)504,timeout,于是繼續(xù)看nginx的日志,懷疑是不是沒生效,但是查看日志之后發(fā)現(xiàn)報(bào)錯(cuò)狀態(tài)碼變了,是499,并且都是request_time為60s,其實(shí)就相當(dāng)與客戶端的請(qǐng)求打到了Nginx上,Nginx把請(qǐng)求轉(zhuǎn)到后轉(zhuǎn)服務(wù)器A,由于nginx的proxy_connect_timeout 超時(shí)時(shí)間默認(rèn)的60s,就會(huì)導(dǎo)致Nginx把客戶端的請(qǐng)求轉(zhuǎn)到服務(wù)器A的時(shí)候,就會(huì)嘗試連接60s,而客戶端的響應(yīng)時(shí)間設(shè)的是30s,所以造成客戶端造成大量超時(shí)情況,Nginx報(bào)大量的499。
。然后經(jīng)過查閱之后,發(fā)現(xiàn)需要增加參數(shù)proxy_ignore_client_abort修改為on,想看看真實(shí)情況,于是在報(bào)錯(cuò)的location下增加了之后reload了nginx。
繼續(xù)觀察日志,發(fā)現(xiàn)日志又變了,是報(bào)504 180s。
此時(shí)開始懷疑是nginx之后的saas端nginx的的問題,然后根據(jù) x-request-id搜索到日志,發(fā)現(xiàn)請(qǐng)求確實(shí)到了saas端,但是很明顯,日志打印出來的200,請(qǐng)求時(shí)長(zhǎng)是60s。
于是根據(jù)上面的鏈路情況,懷疑到了saas端和私有端的saas端slb (5) 上,經(jīng)過客戶核實(shí),他們用的阿里云的slb,默認(rèn)的最大連接請(qǐng)求超時(shí)時(shí)間為180s,基本上和私有端的nginx里面的日志大量出現(xiàn)180s超時(shí)能對(duì)應(yīng)上。
于是提工單給阿里云客服,咨詢是否可以調(diào)大,結(jié)論是不可以,監(jiān)聽器http和https協(xié)議的最大只能180s(其實(shí)人家是有道理的,這完全是由于我們私有端在澳洲,saas端在歐洲,跨洲訪問的結(jié)果),但是客服說可以采用tcp協(xié)議,能夠支持900s,于是新建了一個(gè)tcp協(xié)議的監(jiān)聽器,連接超時(shí)時(shí)間也設(shè)置為350s(為了與nginx上的proxy_read_timeout區(qū)別開),然后把私有端的upstrem轉(zhuǎn)發(fā)的地址端口改成新的測(cè)試,客戶答復(fù)訪問正常。
- 第三次問題排查
是我太天真了,以為完全解決了,但是第二天客戶反饋,隨機(jī)性還是會(huì)出現(xiàn)504超時(shí),期間讓客戶用瀏覽器無痕模式打開,清理瀏覽器緩存,依舊偶爾出現(xiàn),影響客戶體驗(yàn),因此有了第三次問題排查。
依舊先去查看私有端nginx的日志,無異常,狀態(tài)碼都是200,只是請(qǐng)求響應(yīng)時(shí)間比較長(zhǎng)超過60s了。
查看saas端的nginx日志也是正常的。
然后就不理解了,問題出在哪里,然后讓客戶如果再次出現(xiàn),就把報(bào)錯(cuò)接口的copy url出來,然后手動(dòng)在服務(wù)器請(qǐng)求url,能夠復(fù)現(xiàn)出來504,并且是nginx給返回的。
于是在私有端一邊手動(dòng)請(qǐng)求,一邊tcpdump抓包,發(fā)現(xiàn)也是正常的tcp三次握手連接,http正常請(qǐng)求返回,無異常。
但是在請(qǐng)求返回的數(shù)據(jù)上,發(fā)現(xiàn)了一個(gè)端倪,server并不是nginx,我們的nginx因?yàn)樾薷倪^名字,叫Sws,所以剛才請(qǐng)求的時(shí)候nginx 504 timeout,不是我們業(yè)務(wù)側(cè)返回的,然后就懷疑到了請(qǐng)求鏈路上私有端 SLB(2) 上,于是找客戶確認(rèn),訪問的域名雖然走了cdn加速,但是會(huì)回源到這個(gè)slb上,然后監(jiān)聽器的連接超時(shí)時(shí)間設(shè)置的的確是60s,然后客戶修改成180s,之后兩天沒有出現(xiàn)過超時(shí)的問題了。
3.排查過程中的知識(shí)點(diǎn)
3.1 在nginx中 499狀態(tài)碼的定義和處理方法
- 查看Nginx源碼
當(dāng)客戶端主動(dòng)關(guān)閉鏈接時(shí),http狀態(tài)代碼中沒有可以表示該狀態(tài)的,但在nginx又需要記錄,所以自定義了一個(gè)499這個(gè)狀態(tài)來表示。
*
* HTTP does notdefine the code for the case when a client closed
* the connectionwhile we are processing its request so we introduce
* own code to logsuch situation when a client has closed the connection
* before we even tryto send the HTTP header to it
*
*/
#define NGX_HTTP_CLIENT_CLOSED_REQUEST 499
所以顯然,客戶端端主動(dòng)關(guān)閉請(qǐng)求或者客戶端網(wǎng)絡(luò)斷掉時(shí),于是nginx就記錄了499狀態(tài),并且斷開了和后面服務(wù)端的連接(這樣可能導(dǎo)致服務(wù)端返回?cái)?shù)據(jù)時(shí),因?yàn)檫B接斷開而報(bào)錯(cuò))。
- 解決499問題
- 查看服務(wù)端為什么響應(yīng)這么慢,是否需要優(yōu)化,或者調(diào)大客戶端方的連接超時(shí)時(shí)間,不那么快斷開
- proxy_ignore_client_abort參數(shù)調(diào)整。
這個(gè)參數(shù)表示忽略客戶端終止情況,默認(rèn)為off關(guān)閉狀態(tài),當(dāng)客戶端網(wǎng)絡(luò)中斷請(qǐng)求時(shí),nginx 服務(wù)器中斷其對(duì)后端服務(wù)器的請(qǐng)求,并立即記錄 499 日志。
設(shè)置為 on 開啟,則nginx會(huì)忽略客戶端中斷,并一直等著代理服務(wù)執(zhí)行返回,記錄后端返回的請(qǐng)求的狀態(tài)。
location =/api {
proxy_ignore_client_abort on;
proxy_pass http://service_backends;
}
這個(gè)參數(shù)的意思是:在客戶端主動(dòng)關(guān)閉連接后, nginx 與分發(fā)服務(wù)器的連接是否保持連接。如果參數(shù)設(shè)置了on,則客戶端如果斷開連接,nginx也不會(huì)斷開與后端服務(wù)端的連接,nginx會(huì)等待后端處理完(或者超時(shí)),然后記錄「后端的返回信息」到日志。所以,如果后端返回 200,就記錄 200 ;如果后端放回 5XX ,那么就記錄 5XX 。如果超時(shí)(默認(rèn)60s,可以用 proxy_read_timeout 設(shè)置),Nginx 會(huì)主動(dòng)斷開連接,記錄 504。
注意:開啟后nginx只會(huì)在讀取超時(shí)時(shí)關(guān)閉連接,默認(rèn)為60s,可能出現(xiàn)請(qǐng)求連接擠壓的情況,所以默認(rèn)情況下是關(guān)閉。如果開啟必須設(shè)置好proxy_read_timeout超時(shí)時(shí)間,并且nginx最好別做反向代理以外的事情。
這個(gè)方案只是解決了兩個(gè)問題:(1)nginx上499的錯(cuò)誤(2)服務(wù)端因?yàn)檫B接斷開報(bào)Broken pipe的錯(cuò)誤。
所以最好的方法還是優(yōu)化服務(wù)端。
3.2 nginx中的時(shí)間解釋
這個(gè)時(shí)間有沒有取決于nginx的日志格式log_format里是否配置
- request_time:指的就是從接收用戶請(qǐng)求的第一個(gè)字節(jié)到發(fā)送完響應(yīng)數(shù)據(jù)的時(shí)間,即$request_time 包括接收客戶端請(qǐng)求數(shù)據(jù)的時(shí)間、后端程序響應(yīng)的時(shí)間、發(fā)送響應(yīng)數(shù)據(jù)給客戶端的時(shí)間。(request processing time in seconds with a milliseconds resolution; time elapsed between the first bytes were read from the client and the log write after the last bytes were sent to the client 。)
- up_resp_time/upstream_response_time:指nginx從后端獲取結(jié)果的處理時(shí)間,從nginx和后端建立連接開始,到關(guān)閉連接為止,連接的后端地址為upstream_addr值。(keeps times of responses obtained from upstream servers; times are kept in seconds with a milliseconds resolution. Several response times are separated by commas and colons like addresses in the $upstream_addr variable)。
- up_addr/upstream_addr:后端服務(wù)地址。
- request_time時(shí)間肯定是要比up_resp_time要大的。
3.3 nginx中proxy相關(guān)的參數(shù)解釋
proxy_connect_timeout :后端服務(wù)器連接的超時(shí)時(shí)間_發(fā)起握手等候響應(yīng)超時(shí)時(shí)間(代理連接超時(shí))默認(rèn)60s
proxy_read_timeout:它決定了nginx會(huì)等待多長(zhǎng)時(shí)間來獲得請(qǐng)求的響應(yīng)(代理接收超時(shí))默認(rèn)值60s
proxy_send_timeout :后端服務(wù)器數(shù)據(jù)回傳時(shí)間_就是在規(guī)定時(shí)間之內(nèi)后端服務(wù)器必須傳完所有的數(shù)據(jù)(代理發(fā)送超時(shí))默認(rèn)值60s
4.總結(jié)
- 當(dāng)前修改配置參數(shù)實(shí)際上屬于非標(biāo)準(zhǔn)操作,本文只是提供一個(gè)自己在排查過程的思路方向,每個(gè)問題的情況和背景不一樣,需要各自結(jié)合實(shí)際情況來調(diào)整。
- 該問題主要還是跨洲訪問,沒有走專線,網(wǎng)絡(luò)這邊不穩(wěn)定會(huì)導(dǎo)致在請(qǐng)求的時(shí)候出現(xiàn)超時(shí)問題,然后根據(jù)具體的問題現(xiàn)在通過調(diào)整配置來臨時(shí)解決這個(gè)問題,讓客戶能正常使用,客戶是上帝。
- 不要畏懼問題,所有的問題總能找到原因,不能一味的歸結(jié)到是網(wǎng)絡(luò)的問題,重啟大法來解決,我們其實(shí)可以定位得更細(xì),需要知其然知其所以然。