開發與維運

CDN 499 問題排查

背景

先了解下 499 ,本身並不是標準 http 協議規定產生,而是 nginx 代碼中針對網絡情況做的一個特殊定義。先看下 nginx 代碼中的定義(源碼文件 ngx_request_t.h)

/*
* 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

ngx_string(ngx_http_error_495_page), /* 495, https certificate error */
ngx_string(ngx_http_error_496_page), /* 496, https no certificate */
ngx_string(ngx_http_error_497_page), /* 497, http to https */
ngx_string(ngx_http_error_404_page), /* 498, canceled */
ngx_null_string,                     /* 499, client has closed connection */
  • 這是nginx定義的一個狀態碼,用於表示這樣的錯誤:服務器返回http頭之前,客戶端就提前關閉了http連接。
  • 但還有一種可能就行 proxy 到後端的應用處理很慢或者沒有響應。,“客戶端等不及” 所以主動關閉了鏈接

主動斷開

先說第一種場景,客戶端提前斷開。和以下幾個原因有關

1、客戶端應用層的機制主動斷開,無法處理當前的請求。需要結合客戶端的應用層日誌進行分析,最好在客戶的代碼中記錄 socket 的過程,結合應用的埋點日誌。

2、網絡層處理超時 TCP 協議棧主動發起了斷開,需要客戶端能否復現並抓到現場,可以使用 tcpdump 或者 Wireshark 固定本地的端口和其他唯一條件去抓包(常用 tcpdump -i device -s0 host $domain/$ip -w except.pcap)。或者可以用 tcpping 、mtr 初步分析網絡是否有明顯異常。

轉發&處理超時

繼續上傳的 nginx 代碼查找,“NGX_HTTP_CLIENT_CLOSED_REQUEST”,發現目前這個狀態值只在 ngx_upstream 中賦值, upstream在以下幾種情況下會返回 499

upstream 在收到讀寫事件處理之前時,會檢查連接是否可用:

ngx_http_upstream_check_broken_connection,
   if (c->error) { 
       ...
       if (!u->cacheable) { 
           ngx_http_upstream_finalize_request(r, u, NGX_HTTP_CLIENT_CLOSED_REQUEST);
       }
}

1、server處理請求未結束,而client提前關閉了連接,此時也會返回499。這種情況也可能是 CDN 節點在回源到客戶源站時間太長,客戶端等不及斷開。
客戶端可以下載 CDN 日誌過濾下異常的 URL 和時間點分析
if ( responsetime too long )
{
if(http_cache -> MISS)
//說明沒有命中緩存,和回到客戶源站導致的響應時間過長有關係。

if(http_cache -> HIT)
//說明命中節點緩存,需要升級售後繼續進行分析。
}

2、在一個 upstream 出錯,執行 next_upstream 時也會判斷連接是否可用,不可用則返回499。但這種情況基本佔比很少,問題的核心就是要排查為什麼服務端處理時間過長。

建議

1、發現 CDN MISS 回源後響應時間很長而導致 499 ,原站可以看下錯誤日誌,或者代碼的埋點日誌分析,為什麼處理時間會很長。

2、檢查原站是否存在慢 SQL 查詢,或者一些讀寫數據庫導致響應的時間過長。

3、如果原站是 nginx 或者基於 nginx 二次開發的 http server ,可以開啟 proxy_ignore_client_abort on;( 讓代理服務端不要主動關閉客戶端的連接)。客戶端主動斷開連接後,nginx 會等待後端處理完然後返回 2xx ,如果後端處理超時,則返回 5xx 。

4、如果原站是 nginx 或者基於 nginx 二次開發的 http server,可以將 read_timeout write_timeout 調整大一些,要結合主機的負載能力調整,不能設置太長,容易造成 FD 耗盡,或者 socket 不夠分配。

Leave a Reply

Your email address will not be published. Required fields are marked *