天天看點

為什麼我的網站響應速度時快時慢?

問題現象

第一次發現這個問題,應該是在很早之前,我本地用浏覽器通路我們公司的業務網站,發現有時很快,有時達到8、9秒以上, 但是也沒有太在意,以為是偶爾一次的。另外我用手機4G網絡也測試通路了下,速度挺快的。

然而當站點可用性監控系統剛上線營運的時候,就經常收到報警的通知,因為設定了探測 逾時時間,我設定的是5秒。當時覺得可能就這一個站點誤報,我單獨把這個站點的逾時設定了10s,但過了幾天後,報警還是會誤報, 終于忍不住了。

這裡要自我檢讨下,發現問題時,我投機取巧的去掩蓋問題,而沒有去正視這個問題。這種态度是要不得的。不然問題會像滾雪球一樣可怕!!!

現象總結

:公司某個網站監控得到的響應時間,延遲大,非常不穩定, 導緻站點可用性監控經常誤報警

環境介紹分析及測試

環境介紹

這裡我介紹下整體的大緻環境,這幾台業務主機上,每台主機都有一個nginx,用于處理虛拟主機。然後最上面有一個公網LB(負載均衡器)。 它負責接收外部的流量,終止ssl,均衡的分發請求到每個主機的nginx上。

還有一點需要介紹下,我們線上還有一個公網LB,也是轉發流量到這幾台主機nginx上。這倆LB的差別隻是加載的域名ssl證書不一樣,其它配置一摸一樣(讓我一直在糾結是LB配置的原因)。 最後就是監控系統是部署在容器叢集裡的(很大程度的迷惑了自己,我曾以為是容器網絡出現了延時問題…)。

分析

當時想了以下幾個點:

  • 公網LB 配置錯誤
  • 某台應用主機處理請求時間過長
  • 監控系統部署所在叢集的網絡問題
  • 應用主機系統參數有問題(最後追加上去的,我還真沒想到這塊)
測試

公網LB 配置錯誤

針對這個問題,檢查了LB的的配置的逾時時間,緩存等選項,都沒有啥結果,後來想到了日志,在haproxy的日子裡,經常看到這樣的消息。下面是截取的其中一條記錄:

Jun 15 16:45:29 18.19.1.12 haproxy[30952]: 139.1.2.3:61653 [15/Jun/2018:16:45:08.784] lbl-ckv7ynro~ lbl-ckv7ynro_default/lbb-izjpmxrh 327/15003/-1/-1/20331 503 213 - - sCNN 4/3/0/0/+3 0/0 "HEAD /sessions/auth?return_to=%2F HTTP/1.1"      

以前沒咋看過haproxy 日志,nginx日志倒是都明白,這一看傻眼了。查查文檔吧。下面是對每段的解釋:

Jun 15 16:45:29                       # 時間
18.19.1.12                            # haproxy ip
haproxy[30952]:                       # 程序ID
139.1.2.3:61653                       #client_ip+port
[15/Jun/2018:16:45:08.784]            #通過haproxy接收到連接配接的确切日期時間
lbl-ckv7ynro~                         #前端監聽器name
lbl-ckv7ynro_default/lbb-izjpmxrh     #管理與伺服器連接配接的後端/ 命中的 真實server主機
327/15003/-1/-1/20331   
#327 是在接收到第一個位元組後等待來自用戶端(不包括正文)的完整HTTP請求花費的總時間(毫秒)
#15003 是在各種隊列中等待的總時間(毫秒)。如果連接配接在到達隊列之前中止,則可以為“-1”。
#-1 是等待連接配接建立到最終伺服器的總時間(以毫秒為機關),包括重試次數。 如果請求在建立連接配接之前被中止,它可以是“-1”。
#-1 是等待伺服器發送完整HTTP響應的總時間(毫秒),不計算資料。 如果在接收到完整的響應之前請求被中止,它可以是“-1”。 它通常與伺服器的請求處理時間相比對,盡管它可能會被用戶端發送到伺服器的資料量所改變。 “GET”請求的大部分時間通常表示重載的伺服器。
#20331 是請求在haproxy中保持活動的時間,這是在接收到的請求的第一個位元組和發送的最後一個位元組之間經過的總時間(以毫秒為機關)。 它涵蓋所有可能的處理,除了握手(見Th)和空閑時間(參見Ti)
503      #http狀态碼
213      #是發送日志時發送到用戶端的總位元組數
-   #是一個可選的“name = value”條目,訓示用戶端在請求中具有此cookie。 未設定 -表示
-   #是一個可選的“name = value”條目,表示伺服器已經傳回了一個具有響應的cookie。 
sCNN                                  #http格式下,會話狀态,結束時會話的條件:(timeout, error)
#s 在等待伺服器發送或接收資料時,伺服器端逾時已過期
#C 代理正在等待CONNECTION在伺服器上建立。 伺服器最多可能已經注意到連接配接嘗試。
#N client沒有提供cookie。這通常是新的訪客,第一次通路的情況,是以計算在日志中這個标志的出現次數,通常表示站點被頻繁通路的有效趨勢。
#N 伺服器沒有提供cookie,也沒有插入任何cookie。
4/3/0/0/+3
#4 是會話記錄時程序上的并發連接配接總數
#3 是會話記錄時前端上的并發連接配接總數
#0 是會話記錄時由後端處理的并發連接配接的總數
#0 是在會話記錄時伺服器上仍然處于活動狀态的并發連接配接總數
#+3 是嘗試連接配接到伺服器時此會話遇到的連接配接重試次數
0/0   # 前一個數字0 是在伺服器隊列中之前處理的請求的總數,後一個數字0是是在伺服器隊列中之前處理的請求的總數
"HEAD /sessions/auth?return_to=%2F HTTP/1.1"    #是完整的HTTP請求行,包括方法,請求和HTTP版本字元串。      

上面的日志分析中,有很多有價值的資訊,值得關注的有

4/3/0/0/+3

,

sCNN

. 但是當時我隻關注了逾時這一點,以為是網絡原因導緻的用戶端連接配接後端伺服器的逾時,然後就繼續排查了。這裡其實差不多已經 說明了出問題的點:lb 和 後端伺服器之間的故障(後話,我忽略了這一點)。

某台應用主機處理請求時間過長

針對這個的測試,我準備在夜間流量低峰時,暫時禁用掉其它的LB後端,隻保留1台後端主機進行對外服務。很快的我發現這也行不通。問題依舊會出現。逐個的去替換,這一個測試沒效果。

監控系統部署所在叢集的網絡問題

做這個測試時,我還請了外援。讓别人在他們公司内部幫忙用ab 通路下。分别是外部聯通、外部電信、Google雲主機、VPC内部。 下面是ab針對域名通路的結果。 LB1: lb1.example.com LB2: lb2.example.com

LB1 的ab 測試結果:

聯通測試:
Server Software:        nginx
Server Hostname:        lb1.example.com
Server Port:            443
SSL/TLS Protocol:       TLSv1.2,ECDHE-RSA-AES256-GCM-SHA384,2048,256
Document Path:          /favicon_128.ico/
Document Length:        2406 bytes
Concurrency Level:      1
Time taken for tests:   17.629 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Non-2xx responses:      100
Total transferred:      276000 bytes
HTML transferred:       240600 bytes
Requests per second:    5.67 [#/sec] (mean)
Time per request:       176.291 [ms] (mean)
Time per request:       176.291 [ms] (mean, across all concurrent requests)
Transfer rate:          15.29 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       16  121 157.1     20     653
Processing:     8   55 104.7     11     633
Waiting:        8   53  99.1     11     633
Total:         26  176 188.5     36     668
Percentage of the requests served within a certain time (ms)
  50%     36
  66%    241
  75%    245
  80%    248
  90%    452
  95%    658
  98%    663
  99%    668
 100%    668 (longest request)
電信測試:
Server Software:        nginx
Server Hostname:        lb1.example.com
Server Port:            443
SSL/TLS Protocol:       TLSv1.2,ECDHE-RSA-AES256-GCM-SHA384,2048,256
Document Path:          /favicon_128.ico/
Document Length:        2406 bytes
Concurrency Level:      1
Time taken for tests:   69.515 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Non-2xx responses:      100
Total transferred:      276000 bytes
HTML transferred:       240600 bytes
Requests per second:    1.44 [#/sec] (mean)
Time per request:       695.151 [ms] (mean)
Time per request:       695.151 [ms] (mean, across all concurrent requests)
Transfer rate:          3.88 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:      108  120   5.7    121     132
Processing:    40  575 1527.9     45    5049
Waiting:       40  575 1527.9     45    5049
Total:        148  695 1527.5    166    5175
Percentage of the requests served within a certain time (ms)
  50%    166
  66%    171
  75%    173
  80%    174
  90%   5154
  95%   5167
  98%   5173
  99%   5175
 100%   5175 (longest request)      

LB2 的ab 測試結果:

聯通測試:
Server Software:        nginx
Server Hostname:        lb2.example.com
Server Port:            443
SSL/TLS Protocol:       TLSv1.2,ECDHE-RSA-AES256-GCM-SHA384,2048,256
Document Path:          /favicon_128.ico/
Document Length:        2406 bytes
Concurrency Level:      1
Time taken for tests:   17.168 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Non-2xx responses:      100
Total transferred:      276000 bytes
HTML transferred:       240600 bytes
Requests per second:    5.82 [#/sec] (mean)
Time per request:       171.675 [ms] (mean)
Time per request:       171.675 [ms] (mean, across all concurrent requests)
Transfer rate:          15.70 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       16  139 161.6     20     840
Processing:     8   32  87.1     10     641
Waiting:        8   32  87.1     10     641
Total:         25  171 176.9    235     852
Percentage of the requests served within a certain time (ms)
  50%    235
  66%    241
  75%    245
  80%    248
  90%    255
  95%    655
  98%    667
  99%    852
 100%    852 (longest request) 
電信測試:
Server Software:        nginx
Server Hostname:        lb2.example.com
Server Port:            443
SSL/TLS Protocol:       TLSv1.2,ECDHE-RSA-AES256-GCM-SHA384,2048,256
Document Path:          /favicon_128.ico/
Document Length:        2406 bytes
Concurrency Level:      1
Time taken for tests:   16.714 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Non-2xx responses:      100
Total transferred:      276000 bytes
HTML transferred:       240600 bytes
Requests per second:    5.98 [#/sec] (mean)
Time per request:       167.144 [ms] (mean)
Time per request:       167.144 [ms] (mean, across all concurrent requests)
Transfer rate:          16.13 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:      110  122   4.9    122     136
Processing:    40   45   5.8     45     100
Waiting:       40   45   5.8     45      99
Total:        150  167   8.8    166     225
Percentage of the requests served within a certain time (ms)
  50%    166
  66%    170
  75%    171
  80%    172
  90%    175
  95%    176
  98%    183
  99%    225
 100%    225 (longest request)      

VPC 内部測試(統一出口):

LB1 的測試結果:
Server Software:        nginx
Server Hostname:        lb1.example.com
Server Port:            443
SSL/TLS Protocol:       TLSv1.2,ECDHE-RSA-AES256-GCM-SHA384,2048,256
Document Path:          /favicon_128.ico/
Document Length:        2406 bytes
Concurrency Level:      1
Time taken for tests:   513.333 seconds
Complete requests:      100
Failed requests:        1
   (Connect: 0, Receive: 0, Length: 1, Exceptions: 0)
Non-2xx responses:      100
Total transferred:      273453 bytes
HTML transferred:       238302 bytes
Requests per second:    0.19 [#/sec] (mean)
Time per request:       5133.329 [ms] (mean)
Time per request:       5133.329 [ms] (mean, across all concurrent requests)
Transfer rate:          0.52 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        4    5   2.3      4      21
Processing:     4 5128 4147.8   5007   20006
Waiting:        4 5128 4147.8   5007   20006
Total:          9 5133 4147.6   5012   20011
LB2 的測試結果:
Server Software:        nginx
Server Hostname:        lb2.example.com
Server Port:            443
SSL/TLS Protocol:       TLSv1.2,ECDHE-RSA-AES256-GCM-SHA384,2048,256
Document Path:          /favicon_128.ico/
Document Length:        2406 bytes
Concurrency Level:      1
Time taken for tests:   1.099 seconds
Complete requests:      100
Failed requests:        0
Non-2xx responses:      100
Total transferred:      276000 bytes
HTML transferred:       240600 bytes
Requests per second:    91.00 [#/sec] (mean)
Time per request:       10.990 [ms] (mean)
Time per request:       10.990 [ms] (mean, across all concurrent requests)
Transfer rate:          245.26 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        4    5   1.7      4      16
Processing:     4    6   2.4      5      22
Waiting:        4    6   2.4      5      22
Total:          8   11   2.9     10      26
Percentage of the requests served within a certain time (ms)
  50%     10
  66%     11
  75%     11
  80%     12
  90%     14
  95%     17
  98%     24
  99%     26
 100%     26 (longest request)      

google 的測試結果沒有放上來,相比國内通路是慢了點。這裡主要關注了在VPC内部測試時,LB1的結果很不理想。 但是LB2的結果又很正常。看到之前的環境介紹,再配合這個結果,又把我拉回了原來的思路上,即LB 和vpc内部網絡有問題。頭大…. 哦,忘記說了一件事,我也在nginx主機上和vpc 内部其它主機上通過修改請求header,直接通路内部IP請求資源,這裡是沒有問題的,響應特别快。這又能證明主機上服務運作良好。網絡也可以排除。這。。。悲劇了。

應用主機系統參數有問題

後來請教别人,最後告訴我看看核心參數recycle。然後我又去網上查了下,看看有沒有類似的問題,但是不好查啊,環境都不一樣。但是最後查到一條關于nat環境下, 在應用伺服器上設定核心參數時,啟用了

net.ipv4.tcp_tw_recycle

net.ipv4.tcp_tw_reuse

. 按照這個思路 導緻有些用戶端連接配接伺服器失敗,根據這個我去看了下伺服器的配置,果不其然,有這個參數的優化。

驗證結果

在手動的執行下面這條指令後:

sysctl -w net.ipv4.tcp_tw_recycle      

繼續在vpc網絡内部測試,發現對該lb的ab測試全部都正常了。。。。。 真是應了那句話,差不多一切的問題不是網絡問題就是配置問題。。 下面是修改前後的監控對比圖:

為什麼我的網站響應速度時快時慢?

圖1:URL監控響應時間圖

分析根本原因

這裡提出了2個問題,便于了解記憶全文。

  • 為什麼啟用這個tcp recycle?
  • 為什麼啟用tcp recycle之後,會阻塞部分使用者的連接配接請求?

這個是之前在部署應用主機系統時,修改優化了部分核心參數,當時想的是為了增大主機對tcp的連接配接性能,防止遇到并發使用者的連接配接,導緻tcp 連接配接不能快速釋放,進而引發伺服器 出現性能上的瓶頸(會導緻伺服器記憶體和CPU的暴增)。因為client 與server 建立連結傳輸完資料後,會斷開連結,而伺服器這邊還會有2MSL的 time_wait 時間,超過這個時間之後,正常情況下,該socket才會被釋放, 然後才可以接收其它client的請求。因為server 的端口是有固定範圍的,不是說65535個全部都用來建立連接配接( 可參看系統核心配置:net.ipv4.ip_local_port_range)。

另外關于time_wait具體的可以參考tcp 的4次斷開後的狀态。 是以為了快速回收和重新使用,才開啟了 tcp的reuse 和 recycle。但沒想到這個會引起這麼大的問題。 并且這個參數之前确實也在前公司用過。但沒有發現這個類似的問題(也可能是當時的監控不到位,沒有發現也不代表該問題不存在。)。 這裡是官方核心文檔對這倆參數的解釋:

net.ipv4.tcp_tw_recycle

: Enable fast recycling TIME-WAIT sockets. Default value is 0. It should not be changed without advice/request of technical experts.

net.ipv4.tcp_tw_reuse

: Allow to reuse TIME-WAIT sockets for new connections when it is safe from protocol viewpoint. Default value is 0. It should not be changed without advice/request of technical experts.

TIME-WAIT

時間為2MSL的作用就是為了以確定屬于未來連接配接的資料包不會被誤認為是舊連接配接的延遲資料包。

tcp_tw_reuse

可以在

TIME_WAIT

狀态到期之前使用套接字,核心将嘗試確定沒有關于TCP序列号的沖突。 如果啟用tcp_timestamps(預設是開啟的,也就是PAWS,用于保護包裝序列号),它将確定不會發生這些沖突。

為什麼會阻塞用戶端(Nat 環境)請求?

上面說了為什麼啟用它,現在在讨論下,為什麼啟用它後,會導緻部分使用者的連結,而不是全部? 看了一些文檔及别人提到的解釋:

當啟用tcp_tw_recycle時,核心變得更具攻擊性,并将對遠端的client建立連接配接請求時使用的時間戳進行假設。它将跟蹤具有TIME_WAIT狀态連接配接的每個遠端的client使用的最後時間戳, 并允許在時間戳正确增加時重新使用套接字。但是,如果主機使用的時間戳發生更改(即server 上記錄time_wait中有A 用戶端的上一個請求的時間戳是111112,現在又出現一了一個A 用戶端的資料請求,并且序列号是111111.), 則SYN資料包将以靜默的方式丢棄,并且連接配接将無法建立(将看到類似于“connect timeout”的錯誤)。

為什麼我看不到關于connect timeout 的錯誤呢? 因為在這個環境下,client 相當于是最前面的LB,即haproxy,從這裡也很好的解釋了,為什麼haproxy 的日志有很多連接配接重試的記錄! 因為在NAT(網絡位址轉換) 環境下,有很多的client,流量進出都是通過一個公網IP出去的。但當多個NAT 網絡環境下的使用者同時或者短時間内去通路server 端的話, 因為對于server來說,此時我隻知道有一個IP(client的公網IP)與我建立連接配接, 并且這裡啟用了

tcp_tw_recycle

,Server主機核心将認為有一部分連接配接請求的時間戳不是遞增的,那麼核心将判斷該請求是無效的,就會丢失該請求。

To keep the same guarantees the TIME-WAIT state was providing, while reducing the expiration timer, when a connection enters the TIME-WAIT state, the latest timestamp is remembered in a dedicated structure containing various metrics for previous known destinations. Then, Linux will drop any segment from the remote host whose timestamp is not strictly bigger than the latest recorded timestamp, unless the TIME-WAIT state would have expired

總結

不要開啟

tcp_tw_recycle

!!! .重要的話說三遍。 一定要有配置管理!可以友善在系統出問題時,檢查對主機所做的曆史記錄。考慮問題不能光從 軟體程式、網絡上,還要加入對主機系統的分析。tcpdump、ss、還有log等。

另外,針對web 應用服務系統幾個優化建議:

  • 設定

    net.ipv4.ip_local_port_range

    合理的範圍(10000~63000)
  • 給LB設定更多的client IP。
  • 配置LB 有多個外網IP。

引自:

Vincent Bernat

參考

Coping with the TCP TIME-WAIT state on busy Linux servers Vincent Bernat 的翻譯中文版 StackOverflow-Dropping of connections with tcp_tw_recycle

繼續閱讀