自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Go 超時(shí)引發(fā)大量 Fin-Wait2

開(kāi)發(fā) 后端
通過(guò)grafana監(jiān)控面板,發(fā)現(xiàn)了幾個(gè)高頻的業(yè)務(wù)緩存節(jié)點(diǎn)出現(xiàn)了大量的fin-wait2,而且fin-wait2狀態(tài)持續(xù)了不短的時(shí)間。通過(guò)連接的ip地址和抓包數(shù)據(jù)判斷出對(duì)端的業(yè)務(wù)。

[[410689]]

本文轉(zhuǎn)載自微信公眾號(hào)「碼農(nóng)桃花源」,作者峰云就她了。轉(zhuǎn)載本文請(qǐng)聯(lián)系碼農(nóng)桃花源公眾號(hào)。

通過(guò)grafana監(jiān)控面板,發(fā)現(xiàn)了幾個(gè)高頻的業(yè)務(wù)緩存節(jié)點(diǎn)出現(xiàn)了大量的fin-wait2,而且fin-wait2狀態(tài)持續(xù)了不短的時(shí)間。通過(guò)連接的ip地址和抓包數(shù)據(jù)判斷出對(duì)端的業(yè)務(wù)。除此之外,頻繁地去創(chuàng)建新連接,我們對(duì)golang net/http transport的連接池已優(yōu)化過(guò),但established已建連的連接沒(méi)有得到復(fù)用。

另外,隨之帶來(lái)的問(wèn)題是大量time-wait的出現(xiàn),畢竟fin-wait2在拿到對(duì)端fin后會(huì)轉(zhuǎn)變?yōu)閠ime-wait狀態(tài)。但該狀態(tài)是正常的。

分析問(wèn)題

通過(guò)分析業(yè)務(wù)日志發(fā)現(xiàn)了大量的接口超時(shí)問(wèn)題,連接的地址跟netstat中fin-wait2目的地址是一致的。那么問(wèn)題已經(jīng)明確了,當(dāng)http的請(qǐng)求觸發(fā)超時(shí),定時(shí)器對(duì)連接對(duì)象進(jìn)行了關(guān)閉。這邊都close了,那么連接自然無(wú)法復(fù)用,所以就需要?jiǎng)?chuàng)建新連接,但由于對(duì)端的API接口出現(xiàn)邏輯阻塞,自然就又觸發(fā)了超時(shí),continue。

  1. // xiaorui.cc 
  2.  
  3. Get "http://xxxx": context deadline exceeded (Client.Timeout exceeded while awaiting headers) 
  4.  
  5. Get "http://xxxx": context deadline exceeded (Client.Timeout exceeded while awaiting headers) 
  6.  
  7. Get "http://xxxx": context deadline exceeded (Client.Timeout exceeded while awaiting headers) 

通過(guò)strace追蹤socket的系統(tǒng)調(diào)用,發(fā)現(xiàn)golang的socket讀寫(xiě)超時(shí)沒(méi)有使用setsockopt so_sndtimeo so_revtimeo參數(shù)。

  1. [pid 34262] epoll_ctl(3, EPOLL_CTL_ADD, 6, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=1310076696, u64=140244877192984}}) = 0 
  2. [pid 34265] epoll_pwait(3,  <unfinished ...> 
  3. [pid 34262] <... getsockname resumed>{sa_family=AF_INET, sin_port=htons(45242), sin_addr=inet_addr("127.0.0.1")}, [112->16]) = 0 
  4. [pid 34264] epoll_pwait(3,  <unfinished ...> 
  5. [pid 34262] setsockopt(6, SOL_TCP, TCP_NODELAY, [1], 4 <unfinished ...> 
  6. [pid 34262] setsockopt(6, SOL_SOCKET, SO_KEEPALIVE, [1], 4 <unfinished ...> 
  7. [pid 34264] read(4,  <unfinished ...> 
  8. [pid 34262] setsockopt(6, SOL_TCP, TCP_KEEPINTVL, [30], 4 <unfinished ...> 

代碼分析

通過(guò)net/http源碼可以看到socket的超時(shí)控制是通過(guò)定時(shí)器來(lái)實(shí)現(xiàn)的,在連接的roundTrip方法里有超時(shí)引發(fā)關(guān)閉連接的邏輯。由于http的語(yǔ)義不支持多路復(fù)用,所以為了規(guī)避超時(shí)后再回來(lái)的數(shù)據(jù)造成混亂,索性直接關(guān)閉連接。

當(dāng)觸發(fā)超時(shí)會(huì)主動(dòng)關(guān)閉連接,這里涉及到了四次揮手,作為關(guān)閉方會(huì)發(fā)送fin,對(duì)端內(nèi)核會(huì)回應(yīng)ack,這時(shí)候客戶端從fin-wait1到fin-wait2,而服務(wù)端在close-wait狀態(tài),等待觸發(fā)close syscall系統(tǒng)調(diào)用。服務(wù)端什么時(shí)候觸發(fā)close動(dòng)作?需要等待net/http handler業(yè)務(wù)邏輯執(zhí)行完畢。

  1. // xiaorui.cc 
  2.  
  3. var errTimeout error = &httpError{err: "net/http: timeout awaiting response headers", timeout: true
  4.  
  5. func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err error) { 
  6.     for { 
  7.         testHookWaitResLoop() 
  8.         select { 
  9.         case err := <-writeErrCh: 
  10.             if debugRoundTrip { 
  11.                 req.logf("writeErrCh resv: %T/%#v", err, err) 
  12.             } 
  13.             if err != nil { 
  14.                 pc.close(fmt.Errorf("write error: %v", err)) 
  15.                 return nil, pc.mapRoundTripError(req, startBytesWritten, err) 
  16.             } 
  17.             if d := pc.t.ResponseHeaderTimeout; d > 0 { 
  18.                 if debugRoundTrip { 
  19.                     req.logf("starting timer for %v", d) 
  20.                 } 
  21.                 timer := time.NewTimer(d) 
  22.                 defer timer.Stop() // prevent leaks 
  23.                 respHeaderTimer = timer.C 
  24.             } 
  25.         case <-pc.closech: 
  26.             ... 
  27.         case <-respHeaderTimer: 
  28.             if debugRoundTrip { 
  29.                 req.logf("timeout waiting for response headers."
  30.             } 
  31.             pc.close(errTimeout) 
  32.             return nil, errTimeout 

如何解決

要么加大客戶端的超時(shí)時(shí)間,要么優(yōu)化對(duì)端的獲取數(shù)據(jù)的邏輯,總之減少超時(shí)的觸發(fā)。這個(gè)問(wèn)題其實(shí)跟 Go 沒(méi)有關(guān)系,換成openresyt和python同樣有這個(gè)問(wèn)題。

 

責(zé)任編輯:武曉燕 來(lái)源: 碼農(nóng)桃花源
相關(guān)推薦

2022-02-25 14:12:10

熔斷Sentinel微服務(wù)

2020-05-07 11:00:24

Go亂碼框架

2021-01-20 10:16:26

高并發(fā)數(shù)據(jù)服務(wù)

2023-04-11 22:01:57

2013-07-25 10:30:02

亞馬遜數(shù)據(jù)中心

2021-04-26 09:40:46

QPS數(shù)據(jù)庫(kù)Redis

2020-04-02 07:31:53

RPC超時(shí)服務(wù)端

2017-06-09 10:16:40

2021-08-02 22:31:24

Go語(yǔ)言Append

2021-09-30 14:23:23

服務(wù)器開(kāi)發(fā)工具

2015-04-02 09:43:17

2021-10-28 19:35:48

Go 控制超時(shí)

2022-01-09 17:38:32

goHttpClient 集群

2012-05-15 09:49:03

TIME_WAITMySQL

2020-08-06 10:12:20

TCP連接網(wǎng)絡(luò)協(xié)議

2020-10-15 12:26:28

黑客勒索軟件攻擊

2021-07-22 16:19:11

Android 12谷歌用戶

2025-04-23 09:36:23

2021-09-05 11:20:04

帶寬網(wǎng)絡(luò)排查

2021-05-20 06:03:20

比特幣區(qū)塊鏈加密貨幣
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)