golang服務(wù)報(bào)錯(cuò):?write:?broken?pipe的解決方案
一、程序報(bào)錯(cuò)
發(fā)現(xiàn)BSC節(jié)點(diǎn)報(bào)錯(cuò): write: broken pipe
2022/04/11 11:23:00 http: panic serving 172.31.34.109:32952: write tcp 172.31.6.64:9093->172.31.34.109:32952: write: broken pipe
goroutine 145578 [running]:
net/http.(*conn).serve.func1(0xc000399720)
/usr/local/go/src/net/http/server.go:1824 +0x153
panic(0xfbc720, 0xc008f36be0)
/usr/local/go/src/runtime/panic.go:971 +0x499
github.com/gin-gonic/gin/render.JSON.Render(...)
/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/render/json.go:56
github.com/gin-gonic/gin.(*Context).Render(0xc00621d600, 0xc8, 0x133ab98, 0xc00be29b10)
/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/context.go:927 +0x149
github.com/gin-gonic/gin.(*Context).JSON(...)
/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/context.go:970
bsc-wallet/router/apicommon.ReturnSuccessResponse(0xc00621d600, 0x0, 0x10c16c7, 0x7, 0xee3960, 0xc00ae84d20)
/go/src/bsc-wallet/router/apicommon/common.go:38 +0xc5
bsc-wallet/router/handler.BlockInfo(0xc00621d600)
/go/src/bsc-wallet/router/handler/wallethandler.go:395 +0x27e
github.com/gin-gonic/gin.(*Context).Next(...)
/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/context.go:168
github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc000120340, 0xc00621d600)
/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/gin.go:555 +0x2b0
github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc000120340, 0x133ec38, 0xc0067767e0, 0xc00621d500)
/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/gin.go:511 +0x16b
net/http.serverHandler.ServeHTTP(0xc0001881c0, 0x133ec38, 0xc0067767e0, 0xc00621d500)
/usr/local/go/src/net/http/server.go:2887 +0xa3
net/http.(*conn).serve(0xc000399720, 0x13405b8, 0xc0059e4a40)
/usr/local/go/src/net/http/server.go:1952 +0x8cd
created by net/http.(*Server).Serve
/usr/local/go/src/net/http/server.go:3013 +0x39b
這個(gè)錯(cuò)誤的字面意思為:客戶端可能會(huì)在收到服務(wù)端響應(yīng)之前斷開(kāi)連接
二、問(wèn)題原因
想到的問(wèn)題原因可能是以下兩種原因:
- 服務(wù)端所在的服務(wù)器,超過(guò)了最大連接數(shù)
- 客戶端在接收到服務(wù)端響應(yīng)之前斷開(kāi)連接
2.1 連接數(shù)過(guò)大
當(dāng)我們有一些高并發(fā)請(qǐng)求量環(huán)境時(shí),會(huì)遇到來(lái)自于系統(tǒng)級(jí)別的連接數(shù)限制問(wèn)題,這是因?yàn)镃entOS根據(jù)系統(tǒng)硬件信息自己默認(rèn)初始了一個(gè)限制連接數(shù)量,往往這個(gè)數(shù)量是我們遇到的問(wèn)題,所以今天我們需要修改系統(tǒng)的默認(rèn)值來(lái)達(dá)到我們需要的要求,解決一定的高并發(fā)產(chǎn)生的連接數(shù)問(wèn)題。
使用以下命令查看當(dāng)前最大連接數(shù):
# ulimit -n 1024
臨時(shí)修改: 該方法只在當(dāng)前起作用
# ulimit -n 65535 # ulimit -n 65535
永久修改:修改以下配置文件
# vim /etc/security/limits.conf * soft nofile 65535 * hard nofile 65535 * soft nproc 65535 * hard nproc 65535
即便你使用limit -n 1024 等這樣的修改是無(wú)法其效果的,
以及各種修改可能都不起作用:
經(jīng)過(guò)我們的大量研究最終解決了該問(wèn)題
2.2 調(diào)用者在接收到服務(wù)端響應(yīng)之前斷開(kāi)連接
該問(wèn)題可能是因?yàn)椋篶lient端獲取響應(yīng)數(shù)據(jù),突然異常退出或直接close連接。
client退出后,server將會(huì)發(fā)送兩次數(shù)據(jù),server第一次發(fā)送給client,client返回給server RST。第二次在這個(gè)RST的連接上,server繼續(xù)發(fā)送,出現(xiàn)broken pipe。即如下錯(cuò)誤:
2022/04/11 12:27:53 http: panic serving 172.31.34.109:45842: write tcp 172.31.6.64:9093->172.31.34.109:45842: write: broken pipe
那么此時(shí)就會(huì)出現(xiàn)兩個(gè)問(wèn)題,server端遲遲沒(méi)有響應(yīng)數(shù)據(jù)給client端,那么也有可能是以下兩個(gè)原因:
server端去節(jié)點(diǎn)請(qǐng)求數(shù)據(jù)的時(shí)候,節(jié)點(diǎn)一直沒(méi)有返回連接數(shù)過(guò)高,請(qǐng)求需要排隊(duì)處理,可能有的請(qǐng)求還沒(méi)有處理完,就已經(jīng)請(qǐng)求超時(shí)了
關(guān)于此問(wèn)題已向client端進(jìn)行求證,目前連接server端設(shè)置的超時(shí)時(shí)間是:連接超時(shí):0.5秒,響應(yīng)超時(shí)5秒
2.2.1 排查服務(wù)器上的連接數(shù)
查看系統(tǒng)連接數(shù)
# 獲取當(dāng)前socket連接狀態(tài)統(tǒng)計(jì)信息 # cat /proc/net/sockstat sockets: used 1138 TCP: inuse 21 orphan 0 tw 0 alloc 786 mem 220 UDP: inuse 4 mem 2 UDPLITE: inuse 0 RAW: inuse 0 FRAG: inuse 0 memory 0 # 統(tǒng)計(jì)當(dāng)前各種狀態(tài)的連接的數(shù)量的命令 # netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' CLOSE_WAIT 1020 ESTABLISHED 59
查看端口范圍
# 允許系統(tǒng)打開(kāi)的端口范圍,用于向外鏈接的端口范圍 # cat /proc/sys/net/ipv4/ip_local_port_range 32768 60999
查看文件打開(kāi)數(shù)
# 查看當(dāng)前打開(kāi)文件數(shù) # 如果執(zhí)行緩慢,那么執(zhí)行結(jié)果顯示系統(tǒng)當(dāng)前打開(kāi)文件數(shù)500w # lsof | wc -l 4792741 # 把當(dāng)前打開(kāi)文件列表保存 # lsof>>/tmp/lsof.log # 查看 5-6萬(wàn) 行之間的數(shù)據(jù) sed -n '50000,60000p' /tmp/lsof.log
結(jié)果,bsc,且沒(méi)有關(guān)閉:
COMMAND PID PPID USER PGID FD DEVICE SIZE NODE NAME
進(jìn)程名稱 進(jìn)程標(biāo)識(shí)符 父進(jìn)程標(biāo)識(shí)符 進(jìn)程所有者 進(jìn)程所屬組 文件描述符 指定磁盤名稱 文件大小 索引節(jié)點(diǎn) 打開(kāi)文件的確切名稱
bsc-wallet 2105 2125 root 1872u sock 0,7 0t0 2800355 protocol: TCPv6
bsc-wallet 2105 2125 root 1873u sock 0,7 0t0 2802021 protocol: TCPv6
bsc-wallet 2105 2125 root 1874u sock 0,7 0t0 2765499 protocol: TCPv6
bsc-wallet 2105 2125 root 1875u sock 0,7 0t0 2803112 protocol: TCPv6
bsc-wallet 2105 2125 root 1876u sock 0,7 0t0 2769274 protocol: TCPv6
bsc-wallet 2105 2125 root 1877u sock 0,7 0t0 2803114 protocol: TCPv6
bsc-wallet 2105 2125 root 1878u sock 0,7 0t0 2770305 protocol: TCPv
2.2.2 查看連接狀態(tài)為CLOSE_WAIT的連接情況
統(tǒng)計(jì)當(dāng)前各種狀態(tài)的連接的數(shù)量的命令
# netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' CLOSE_WAIT 1169 ESTABLISHED 84
查看CLOSE_WAIT是哪臺(tái)服務(wù)器請(qǐng)求的,以及請(qǐng)求數(shù)
# netstat -a |grep "CLOSE_WAIT"|awk '{print $5}'|awk -F '.' '{print $1}' |sort | uniq -c | sort -nr 1382 ip-172-31-34-109
2.2.3 延時(shí)測(cè)試
可以看到情況為:另外一臺(tái)服務(wù)器上面的服務(wù)(客戶端)請(qǐng)求到bsc-clinet(服務(wù)端)時(shí),由于在他設(shè)置的超時(shí)時(shí)間內(nèi)未返回請(qǐng)求數(shù)據(jù),客戶端主動(dòng)關(guān)閉了連接,導(dǎo)致服務(wù)端所在的服務(wù)器出現(xiàn)大量關(guān)于該服務(wù)端的CLOSE_WAIT的連接狀態(tài),最后服務(wù)端無(wú)法提供服務(wù),報(bào)錯(cuò): write: broken pipe
服務(wù)端未出現(xiàn) CLOSE_WAIT 時(shí)的響應(yīng)時(shí)間及延時(shí)
服務(wù)端目前沒(méi)有CLOSE_WAIT連接狀態(tài)
# netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' ESTABLISHED 21
獲取服務(wù)端請(qǐng)求響應(yīng)時(shí)間
# curl -o /dev/null -s -w %{time_namelookup}::%{time_connect}::%{time_starttransfer}::%{time_total}::%{speed_download}"\n" -d "number=16619272&sign=b8aaab58e4d73753430c473ba29756d3" http://127.0.0.1:9093/api/block/blockInfo 0.000::0.000::0.088::0.088::1572245.000
-o:把curl 返回的html、js 寫到垃圾回收站[ /dev/null]
-s:去掉所有狀態(tài)
-w:按照后面的格式寫出rt
time_namelookup:DNS 解析域名www.36nu.com的時(shí)間
time_commect:client和server端建立TCP 連接的時(shí)間
time_starttransfer:從client發(fā)出請(qǐng)求;到web的server 響應(yīng)第一個(gè)字節(jié)的時(shí)間
time_total:client發(fā)出請(qǐng)求;到web的server發(fā)送會(huì)所有的相應(yīng)數(shù)據(jù)的時(shí)間
speed_download:下周速度 單位 byte/s上面這條命令及返回結(jié)果可以這么理解:
0.000: DNS 服務(wù)器解析www.36nu.com 的時(shí)間單位是s
0.000: client發(fā)出請(qǐng)求,到c/s 建立TCP 的時(shí)間;里面包括DNS解析的時(shí)間
0.088: client發(fā)出請(qǐng)求;到s響應(yīng)發(fā)出第一個(gè)字節(jié)開(kāi)始的時(shí)間;包括前面的2個(gè)時(shí)間
0.088: client發(fā)出請(qǐng)求;到s把響應(yīng)的數(shù)據(jù)全部發(fā)送給client;并關(guān)閉connect的時(shí)間
1572245.000 :下載數(shù)據(jù)的速度
建立TCP連接到server返回client第一個(gè)字節(jié)的時(shí)間:0.088s – 0.000s = 0.088s
server把響應(yīng)數(shù)據(jù)發(fā)送給client的時(shí)間:0.088s – 0.088s = 0.000s
模擬客戶端超時(shí)時(shí)間:連接超時(shí):1秒,響應(yīng)超時(shí)5秒
使用CURL時(shí),有兩個(gè)超時(shí)時(shí)間:一個(gè)是連接超時(shí)時(shí)間,另一個(gè)是數(shù)據(jù)傳輸?shù)淖畲笤试S時(shí)間。
連接超時(shí)時(shí)間用–connect-timeout參數(shù)來(lái)指定,數(shù)據(jù)傳輸?shù)淖畲笤试S時(shí)間用-m參數(shù)來(lái)指定。
# curl --connect-timeout 1 -m 5 -d "number=16619272&sign=b8aaab58e4d73753430c473ba29756d3" http://127.0.0.1:9093/api/block/blockInfo
超時(shí)時(shí)間沒(méi)有問(wèn)題,可以返回?cái)?shù)據(jù)
服務(wù)端出現(xiàn) CLOSE_WAIT 時(shí)的響應(yīng)時(shí)間及延時(shí)
服務(wù)端目有大量有CLOSE_WAIT連接狀態(tài)
# netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' CLOSE_WAIT 1185 ESTABLISHED 31
獲取服務(wù)端請(qǐng)求響應(yīng)時(shí)間
# curl -o /dev/null -s -w %{time_namelookup}::%{time_connect}::%{time_starttransfer}::%{time_total}::%{speed_download}"\n" -d "number=16619272&sign=b8aaab58e4d73753430c473ba29756d3" http://127.0.0.1:9093/api/block/blockInfo 無(wú)法請(qǐng)求到服務(wù)端
模擬客戶端超時(shí)
連接超時(shí)的話,出錯(cuò)提示形如:
curl: (7) Failed connect to 127.0.0.1:9093; Connection refused
數(shù)據(jù)傳輸?shù)淖畲笤试S時(shí)間超時(shí)的話,出錯(cuò)提示形如:
curl: (28) Operation timed out after 30001 milliseconds with 0 out of -1 bytes received
三、解決方法
服務(wù)端出現(xiàn)大量的CLOSE_WAIT
連接狀態(tài),并且都是客戶端請(qǐng)求過(guò)來(lái)的,說(shuō)明是客戶端主動(dòng)關(guān)閉該連接,客戶端需要修改以下兩個(gè)問(wèn)題:
- 客戶端必須使用HTTP長(zhǎng)連接池
- 客戶端設(shè)置的響應(yīng)超時(shí)時(shí)間5秒過(guò)于理想化,應(yīng)修改該超時(shí)時(shí)間
當(dāng)然,服務(wù)端此時(shí)需要請(qǐng)求其它服務(wù)獲取數(shù)據(jù)返回給客戶端,所以服務(wù)端請(qǐng)求時(shí),也必須使用HTTP長(zhǎng)連接池,延時(shí)可能也會(huì)影響該問(wèn)題的出現(xiàn),不過(guò)最主要的原因還是在客戶端上,先修改以上兩個(gè)問(wèn)題即可。
希望大家通過(guò)以上方式可以解決自己的實(shí)際需求,解決自己目前所遇到的問(wèn)題。
到此這篇關(guān)于golang服務(wù)報(bào)錯(cuò): write: broken pipe解決的文章就介紹到這了,更多相關(guān)golang服務(wù)報(bào)錯(cuò)write: broken pipe內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
go語(yǔ)言中的udp協(xié)議及TCP通訊實(shí)現(xiàn)示例
這篇文章主要為大家介紹了go語(yǔ)言中的udp協(xié)議及TCP通訊的實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04Golang實(shí)現(xiàn)自己的Redis(有序集合跳表)實(shí)例探究
這篇文章主要為大家介紹了Golang實(shí)現(xiàn)自己的Redis(有序集合跳表)實(shí)例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01Golang實(shí)現(xiàn)AES加密和解密的示例代碼
AES( advanced encryption standard)使用相同密鑰進(jìn)行加密和解密,也就是對(duì)稱加密。本文將詳細(xì)講解Golang實(shí)現(xiàn)AES加密和解密的方法,感興趣的可以學(xué)習(xí)一下2022-05-05用Go語(yǔ)言標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)Web服務(wù)之創(chuàng)建路由
在上一節(jié)中創(chuàng)建了項(xiàng)目,這篇文章主要介紹如何用Go語(yǔ)言標(biāo)準(zhǔn)庫(kù)創(chuàng)建路由,文中有詳細(xì)的代碼示例,對(duì)大家的學(xué)習(xí)或工作有一定的幫助,感興趣的同學(xué)可以參考下2023-05-05

go語(yǔ)言處理TCP拆包/粘包的具體實(shí)現(xiàn)

如何使用Golang創(chuàng)建與讀取Excel文件

go語(yǔ)言interface接口繼承多態(tài)示例及定義解析

golang如何實(shí)現(xiàn)三元運(yùn)算符功能