1. 程式人生 > 實用技巧 >Connection reset by peer 全解

Connection reset by peer 全解


知識鋪墊

瘋狂創客圈 《SpringCloud Nginx 高併發核心程式設計》Java 工程師/ 架構師 必備【連結

1錯誤資訊

Connection reset by peer

nginx的錯誤日誌中會出現

Connection reset by peer) while reading response header from upstream, client: 1.1.1.1, server: 102.local, request: "GET / HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000

日誌檢視方法

這個錯誤是在nginx的錯誤日誌中發現的,為了更全面的掌握nginx執行的異常,強烈建議在nginx的全域性配置中增加

error_log   logs/error.log notice;

這樣,就可以記錄nginx的詳細異常資訊。

2 原因一:連線已經被上游close。

服務端確實已經關閉了連線: upstream傳送了RST,將連線重置。

errno = 104 錯誤表明你在對一個對端socket已經關閉的的連線呼叫write或send方法,在這種情況下,呼叫write或send方法後,對端socket便會向本端socket傳送一個RESET訊號,在此之後如果繼續執行write或send操作,就會得到errno為104,錯誤描述為connection reset by peer。

如果對方socket已經執行了close的操作,本端socket還繼續在這個連線上收發資料,就會觸發對端socket傳送RST報文。按照TCP的四次握手原理,這時候本端socket應該也要開始執行close的操作流程了,而不是接著收發資料。

  • 比如,當後端為php程式時,如果php執行較慢,並超出php-fpm.conf的request_terminate_timeout設定的秒數。request_terminate_timeout用於設定當某個php指令碼執行最長時間,若超出php-fpm程序管理器強行中止當前程式,並關閉fastcgi和nginx的網路連線,然後nginx中就會出現Connection reset by peer的錯誤了。

也就是說,產生這個錯誤的原因是:php 程式的執行時間超出request_terminate_timeout設定的值。

在php-fpm環境下,在php的安裝目錄的etc/php-fpm.conf中有此值的設定項,可將其設定為0或更大的值。這樣將php的request_terminate_timeout設定為較大的值或0,可減少因php指令碼執行時行過長導致nginx產生Connection reset by peer錯誤。

  • 比如,當後端為java 程式時

java 的也類似,不能Java端主動關閉連線。 如果上游的tomcat 或者 netty 已經關閉連線, 那麼nginx 肯定就是 Connection reset by peer

3 原因二:資料長度不一致

​ 傳送端和接收端事先約定好的資料長度不一致導致的,接收端被通知要收的資料長度小於傳送端實際要傳送的資料長度。

4 原因三: FastCGI 快取小,timeout太小。

nginx的buffer太小,timeout太小。主要指php的環境,nginx如果要解析php指令碼語言,就必須通過配置fastcgi模組來提供對php支援。

問題概述:圖片bit 64生成資料流太大,導致小程式分享彈窗的二維碼圖片生成失敗

nginx http模組新增以下引數配置:

fastcgi_buffer_size 128k;

fastcgi_buffers 4 128k;

fastcgi_busy_buffers_size 128k;

fastcgi_temp_file_write_size 128k;

後臺報錯:

排查:

Client------>nginx------->h5------>nginx---------->client

客戶端通過h5的nginx頁面點選,nginx反向代理到h5 [無異常]

h5通過客戶端請求調取相應介面 [無異常]

介面返回資料通過nginx展示給客戶端 [異常]

Ps: 圖片通過bit 64解析生成返回給客戶端,由於資料長度太長導致

解決方法:

調整nginx配置檔案引數,修改後引數:

fastcgi_buffer_size 256k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;

先簡單的說一下 Nginx 的 buffer 機制,對於來自 FastCGI Server 的 Response,Nginx 將其緩衝到記憶體中,然後依次傳送到客戶端瀏覽器。緩衝區的大小由 fastcgi_buffers 和 fastcgi_buffer_size 兩個值控制。

比如如下配置:

fastcgi_buffers      8 4K;
fastcgi_buffer_size  4K;

fastcgi_buffers 控制 nginx 最多建立 8 個大小為 4K 的緩衝區,而 fastcgi_buffer_size 則是處理 Response 時第一個緩衝區的大小,不包含在前者中。所以總計能建立的最大記憶體緩衝區大小是 84K+4K = 36k。而這些緩衝區是根據實際的 Response 大小動態生成的,並不是一次性建立的。比如一個 8K 的頁面,Nginx 會建立 24K 共 2 個 buffers。

當 Response 小於等於 36k 時,所有資料當然全部在記憶體中處理。如果 Response 大於 36k 呢?fastcgi_temp 的作用就在於此。多出來的資料會被臨時寫入到檔案中,放在這個目錄下面。同時你會在 error.log 中看到一條類似 warning。

顯然,緩衝區設定的太小的話,Nginx 會頻繁讀寫硬碟,對效能有很大的影響,但也不是越大越好,沒意義,呵呵!

FastCGI緩衝設定主要引數

fastcgi_buffers 4 64k

這個引數指定了從FastCGI程序到來的應答,本地將用多少和多大的緩衝區讀取,假設一個PHP或JAVA指令碼所產生頁面大小為256kb,那麼會為其分配4個64kb的緩衝來快取;若頁面大於256kb,那麼大於256kb的部分會快取到fastcgi_temp指定路徑中,這並非是個好辦法,記憶體資料處理快於硬碟,一般該值應該為站點中PHP或JAVA指令碼所產生頁面大小中間值,如果站點大部分指令碼所產生的頁面大小為256kb,那麼可把值設定為16 16k,4 64k等。

fastcgi_buffer_size=64k

讀取fastcgi應答第一部分需要多大緩衝區,該值表示使用1個64kb的緩衝區讀取應答第一部分(應答頭),可以設定為fastcgi_buffers選項緩衝區大小。

fastcgi_connect_timeout=300

連線到後端fastcgi超時時間,單位秒,下同。

fastcgi_send_timeout=300

向fastcgi請求超時時間(這個指定值已經完成兩次握手後向fastcgi傳送請求的超時時間)

fastcgi_reAd_timeout=300

接收fastcgi應答超時時間,同理也是2次握手後。

5 原因四: proxy_buffer快取小

原因就是請求的標頭檔案過大導致502錯誤

解決方法就是提高頭部的快取

http{

client_header_buffer_size 5m;

 

location / {

  proxy_buffer_size 128k;
  proxy_busy_buffers_size 192k;
  proxy_buffers 4 192k;

  }

}


原因五:沒有設定keepalive

​ ngx_http_upstream_check_module這個模組,在使用tcp檢測後端狀態時,只進行了TCP的三次握手,沒有主動斷開這個連線,而是等待服務端來斷開。當後端是nginx或者tomcat時(linux上),超時後後端會發fin包關閉這個連線。這個錯誤日誌recv() failed (104: Connection reset by peer)是在後端為IIS的情況下丟擲的,抓包發現IIS並不會發fin包來斷開連結,而是在超時後發RST包重置連線,所以導致了這個問題。
​ 從這個問題也反應出ngx_http_upstream_check_module這個模組還是需要完善下檢測機制的,如果是在檢測後端狀態後主動關閉這個連線,應該就不會出現connect reset這個問題

通過修改原始碼已經解決了該問題

static ngx_check_conf_t ngx_check_types[] = {
{ NGX_HTTP_CHECK_TCP,
ngx_string("tcp"),
ngx_null_string,
0,
ngx_http_upstream_check_peek_handler,
ngx_http_upstream_check_peek_handler,
NULL,
NULL,
NULL,
0,
1 },

將最後一行的1改為0即可,根據資料結構分析可得知,這個1代表啟用keepalived,所以客戶端才不會主動斷開連線,因為這是tcp的埠連通性檢查,不需要keepalived,將其改為0禁止keepalived即可。

修改之後的程式碼如下:

static ngx_check_conf_t ngx_check_types[] = {
{ NGX_HTTP_CHECK_TCP,
ngx_string("tcp"),
ngx_null_string,
0,
ngx_http_upstream_check_peek_handler,
ngx_http_upstream_check_peek_handler,
NULL,
NULL,
NULL,
0,
0 },

原因六:設定lingering_close

​ 即使你禁用了 http keepalive,nginx 仍然會嘗試處理 HTTP 1.1 pipeline 的請求。你可以配置
lingering_close off 禁用此行為,但這不是推薦的做法,因為會違反 HTTP 協議。見

http://nginx.org/en/docs/http/ngx_http_core_module.html#lingering_close

nginx快速定位異常

錯誤資訊 錯誤說明
"upstream prematurely(過早的) closed connection" 請求uri的時候出現的異常,是由於upstream還未返回應答給使用者時使用者斷掉連線造成的,對系統沒有影響,可以忽略
"recv() failed (104: Connection reset by peer)" (1)伺服器的併發連線數超過了其承載量,伺服器會將其中一些連線Down掉; (2)客戶關掉了瀏覽器,而伺服器還在給客戶端傳送資料; (3)瀏覽器端按了Stop
"(111: Connection refused) while connecting to upstream" 使用者在連線時,若遇到後端upstream掛掉或者不通,會收到該錯誤
"(111: Connection refused) while reading response header from upstream" 使用者在連線成功後讀取資料時,若遇到後端upstream掛掉或者不通,會收到該錯誤
"(111: Connection refused) while sending request to upstream" Nginx和upstream連線成功後傳送資料時,若遇到後端upstream掛掉或者不通,會收到該錯誤
"(110: Connection timed out) while connecting to upstream" nginx連線後面的upstream時超時
"(110: Connection timed out) while reading upstream" nginx讀取來自upstream的響應時超時
"(110: Connection timed out) while reading response header from upstream" nginx讀取來自upstream的響應頭時超時
"(110: Connection timed out) while reading upstream" nginx讀取來自upstream的響應時超時
"(104: Connection reset by peer) while connecting to upstream" upstream傳送了RST,將連線重置
"upstream sent invalid header while reading response header from upstream" upstream傳送的響應頭無效
"upstream sent no valid HTTP/1.0 header while reading response header from upstream" upstream傳送的響應頭無效
"client intended to send too large body" 用於設定允許接受的客戶端請求內容的最大值,預設值是1M,client傳送的body超過了設定值
"reopening logs" 使用者傳送kill -USR1命令
"gracefully shutting down", 使用者傳送kill -WINCH命令
"no servers are inside upstream" upstream下未配置server
"no live upstreams while connecting to upstream" upstream下的server全都掛了
"SSL_do_handshake() failed" SSL握手失敗
"ngx_slab_alloc() failed: no memory in SSL session shared cache" ssl_session_cache大小不夠等原因造成
"could not add new SSL session to the session cache while SSL handshaking" ssl_session_cache大小不夠等原因造成

參考:

https://github.com/alibaba/tengine/issues/901

https://my.oschina.net/u/1024107/blog/1838968

https://blog.csdn.net/zjk2752/article/details/21236725

http://nginx.org/en/docs/http/ngx_http_core_module.html#lingering_close

https://blog.csdn.net/crj121624/article/details/79956283t/u/1024107/blog/1838968

https://blog.csdn.net/zjk2752/article/details/21236725

http://nginx.org/en/docs/http/ngx_http_core_module.html#lingering_close

https://blog.csdn.net/crj121624/article/details/79956283

回到◀瘋狂創客圈

瘋狂創客圈 - Java高併發研習社群,為大家開啟大廠之門