线上问题定位------网络瓶颈
定位丟包,錯包情況
watch more /proc/net/dev用于定位丟包,錯包情況,以便看網(wǎng)絡(luò)瓶頸,重點關(guān)注drop(包被丟棄)和網(wǎng)絡(luò)包傳送的總量,不要超過網(wǎng)絡(luò)上限:
[root@localhost?~]#?watch?-n?2?more?/proc/net/dev Every?2.0s:?more?/proc/net/dev???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????Fri?May??1?17:16:55?2020Inter-|???Receive????????????????????????????????????????????????|??Transmitface?|bytes????packets?errs?drop?fifo?frame?compressed?multicast|bytes????packets?errs?drop?fifo?colls?carrier?compressedlo:???10025?????130????0????0????0?????0??????????0?????????0????10025?????130????0????0????0?????0???????0??????????0ens33:?759098071??569661????0????0????0?????0??????????0?????????0?19335572??225551????0????0????0?????0???????0??????????0-
最左邊的表示接口的名字,Receive表示收包,Transmit表示發(fā)送包;
-
bytes:表示收發(fā)的字節(jié)數(shù);
-
packets:表示收發(fā)正確的包量;
-
errs:表示收發(fā)錯誤的包量;
-
drop:表示收發(fā)丟棄的包量;
查看路由經(jīng)過的地址
traceroute ip可以查看路由經(jīng)過的地址,常用來統(tǒng)計網(wǎng)絡(luò)在各個路由區(qū)段的耗時,如:
[root@localhost?~]#?traceroute?14.215.177.38 traceroute?to?14.215.177.38?(14.215.177.38),?30?hops?max,?60?byte?packets1??CD-HZTK5H2.mshome.net?(192.168.137.1)??0.126?ms?*?*2??*?*?*3??10.250.112.3?(10.250.112.3)??12.587?ms??12.408?ms??12.317?ms4??172.16.227.230?(172.16.227.230)??2.152?ms??2.040?ms??1.956?ms5??172.16.227.202?(172.16.227.202)??11.884?ms??11.746?ms??12.692?ms6??172.16.227.65?(172.16.227.65)??2.665?ms??3.143?ms??2.923?ms7??171.223.206.217?(171.223.206.217)??2.834?ms??2.752?ms??2.654?ms8??182.150.18.205?(182.150.18.205)??5.145?ms??5.815?ms??5.542?ms9??110.188.6.33?(110.188.6.33)??3.514?ms?171.208.199.185?(171.208.199.185)??3.431?ms?171.208.199.181?(171.208.199.181)??10.768?ms 10??202.97.29.17?(202.97.29.17)??29.574?ms?202.97.30.146?(202.97.30.146)??32.619?ms?* 11??113.96.5.126?(113.96.5.126)??36.062?ms?113.96.5.70?(113.96.5.70)??35.940?ms?113.96.4.42?(113.96.4.42)??45.859?ms 12??90.96.135.219.broad.fs.gd.dynamic.163data.com.cn?(219.135.96.90)??35.680?ms??35.468?ms??35.304?ms 13??14.215.32.102?(14.215.32.102)??35.135?ms?14.215.32.110?(14.215.32.110)??35.613?ms?14.29.117.242?(14.29.117.242)??54.712?ms 14??*?14.215.32.134?(14.215.32.134)??49.518?ms?14.215.32.122?(14.215.32.122)??47.652?ms 15??*?*?* ...查看網(wǎng)絡(luò)錯誤
netstat -i可以查看網(wǎng)絡(luò)錯誤:
[root@localhost?~]#?netstat?-i Kernel?Interface?table Iface?????????????MTU????RX-OK?RX-ERR?RX-DRP?RX-OVR????TX-OK?TX-ERR?TX-DRP?TX-OVR?Flg ens33????????????1500???570291??????0??????0?0????????225897??????0??????0??????0?BMRU lo??????????????65536??????130??????0??????0?0???????????130??????0??????0??????0?LRU-
Iface: 網(wǎng)絡(luò)接口名稱;
-
MTU: 最大傳輸單元,它限制了數(shù)據(jù)幀的最大長度,不同的網(wǎng)絡(luò)類型都有一個上限值,如:以太網(wǎng)的MTU是1500;
-
RX-OK:接收時,正確的數(shù)據(jù)包數(shù)。
-
RX-ERR:接收時,產(chǎn)生錯誤的數(shù)據(jù)包數(shù)。
-
RX-DRP:接收時,丟棄的數(shù)據(jù)包數(shù)。
-
RX-OVR:接收時,由于過速(在數(shù)據(jù)傳輸中,由于接收設(shè)備不能接收按照發(fā)送速率傳送來的數(shù)據(jù)而使數(shù)據(jù)丟失)而丟失的數(shù)據(jù)包數(shù)。
-
TX-OK:發(fā)送時,正確的數(shù)據(jù)包數(shù)。
-
TX-ERR:發(fā)送時,產(chǎn)生錯誤的數(shù)據(jù)包數(shù)。
-
TX-DRP:發(fā)送時,丟棄的數(shù)據(jù)包數(shù)。
-
TX-OVR:發(fā)送時,由于過速而丟失的數(shù)據(jù)包數(shù)。
-
Flg:標(biāo)志,B 已經(jīng)設(shè)置了一個廣播地址。L 該接口是一個回送設(shè)備。M 接收所有數(shù)據(jù)包(混亂模式)。N 避免跟蹤。O 在該接口上,禁用ARP。P 這是一個點到點鏈接。R 接口正在運(yùn)行。U 接口處于“活動”狀態(tài)。
包的重傳率
cat /proc/net/snmp用來查看和分析240秒內(nèi)網(wǎng)絡(luò)包量,流量,錯包,丟包。通過RetransSegs和OutSegs來計算重傳率tcpetr=RetransSegs/OutSegs。
[root@localhost?~]#?cat?/proc/net/snmp Ip:?Forwarding?DefaultTTL?InReceives?InHdrErrors?InAddrErrors?ForwDatagrams?InUnknownProtos?InDiscards?InDelivers?OutRequests?OutDiscards?OutNoRoutes?ReasmTimeout?ReasmReqds?ReasmOKs?ReasmFails?FragOKs?FragFails?FragCreates Ip:?1?64?241708?0?0?0?0?0?238724?225517?15?0?0?0?0?0?0?0?0 Icmp:?InMsgs?InErrors?InCsumErrors?InDestUnreachs?InTimeExcds?InParmProbs?InSrcQuenchs?InRedirects?InEchos?InEchoReps?InTimestamps?InTimestampReps?InAddrMasks?InAddrMaskReps?OutMsgs?OutErrors?OutDestUnreachs?OutTimeExcds?OutParmProbs?OutSrcQuenchs?OutRedirects?OutEchos?OutEchoReps?OutTimestamps?OutTimestampReps?OutAddrMasks?OutAddrMaskReps Icmp:?149?0?0?50?99?0?0?0?0?0?0?0?0?0?147?0?147?0?0?0?0?0?0?0?0?0?0 IcmpMsg:?InType3?InType11?OutType3 IcmpMsg:?50?99?147 Tcp:?RtoAlgorithm?RtoMin?RtoMax?MaxConn?ActiveOpens?PassiveOpens?AttemptFails?EstabResets?CurrEstab?InSegs?OutSegs?RetransSegs?InErrs?OutRsts?InCsumErrors Tcp:?1?200?120000?-1?376?6?0?0?4?236711?223186?292?0?4?0 Udp:?InDatagrams?NoPorts?InErrors?OutDatagrams?RcvbufErrors?SndbufErrors?InCsumErrors Udp:?1405?438?0?1896?0?0?0 UdpLite:?InDatagrams?NoPorts?InErrors?OutDatagrams?RcvbufErrors?SndbufErrors?InCsumErrors UdpLite:?0?0?0?0?0?0?0重傳率=292/223186≈0.13%
-
平均每秒新增TCP連接數(shù):通過/proc/net/snmp文件得到最近240秒內(nèi)PassiveOpens的增量,除以240得到每秒的平均增量;
-
機(jī)器的TCP連接數(shù) :通過/proc/net/snmp文件的CurrEstab得到TCP連接數(shù);
-
平均每秒的UDP接收數(shù)據(jù)報:通過/proc/net/snmp文件得到最近240秒內(nèi)InDatagrams的增量,除以240得到平均每秒的UDP接收數(shù)據(jù)報;
-
平均每秒的UDP發(fā)送數(shù)據(jù)報:通過/proc/net/snmp文件得到最近240秒內(nèi)OutDatagrams的增量,除以240得到平均每秒的UDP發(fā)送數(shù)據(jù)報;
超時
超時錯誤大部分處在應(yīng)用層面,所以這塊著重理解概念。超時大體可以分為連接超時和讀寫超時,某些使用連接池的客戶端框架還會存在獲取連接超時和空閑連接清理超時。
-
讀寫超時。readTimeout/writeTimeout,有些框架叫做so_timeout或者socketTimeout,均指的是數(shù)據(jù)讀寫超時。注意這邊的超時大部分是指邏輯上的超時。soa的超時指的也是讀超時。讀寫超時一般都只針對客戶端設(shè)置;
-
連接超時。connectionTimeout,客戶端通常指與服務(wù)端建立連接的最大時間。服務(wù)端這邊connectionTimeout就有些五花八門了,jetty中表示空閑連接清理時間,tomcat則表示連接維持的最大時間;
-
其他。包括連接獲取超時connectionAcquireTimeout和空閑連接清理超時idleConnectionTimeout。多用于使用連接池或隊列的客戶端或服務(wù)端框架。
我們在設(shè)置各種超時時間中,需要確認(rèn)的是盡量保持客戶端的超時小于服務(wù)端的超時,以保證連接正常結(jié)束。
在實際開發(fā)中,我們關(guān)心最多的應(yīng)該是接口的讀寫超時了。
如何設(shè)置合理的接口超時是一個問題。如果接口超時設(shè)置的過長,那么有可能會過多地占用服務(wù)端的tcp連接。而如果接口設(shè)置的過短,那么接口超時就會非常頻繁。
服務(wù)端接口明明rt降低,但客戶端仍然一直超時又是另一個問題。這個問題其實很簡單,客戶端到服務(wù)端的鏈路包括網(wǎng)絡(luò)傳輸、排隊以及服務(wù)處理等,每一個環(huán)節(jié)都可能是耗時的原因。
TCP隊列溢出
tcp隊列溢出是個相對底層的錯誤,它可能會造成超時、rst等更表層的錯誤。因此錯誤也更隱蔽,所以我們單獨(dú)說一說。
如上圖所示,這里有兩個隊列:syns queue(半連接隊列)、accept queue(全連接隊列)。三次握手,在server收到client的syn后,把消息放到syns queue,回復(fù)syn+ack給client,server收到client的ack,如果這時accept queue沒滿,那就從syns queue拿出暫存的信息放入accept queue中,否則按tcp_abort_on_overflow指示的執(zhí)行。
tcp_abort_on_overflow 0表示如果三次握手第三步的時候accept queue滿了那么server扔掉client發(fā)過來的ack。tcp_abort_on_overflow 1則表示第三步的時候如果全連接隊列滿了,server發(fā)送一個rst包給client,表示廢掉這個握手過程和這個連接,意味著日志里可能會有很多connection reset / connection reset by peer。
那么在實際開發(fā)中,我們怎么能快速定位到tcp隊列溢出呢?
-
netstat命令,執(zhí)行netstat -s | egrep "listen|LISTEN"
如上圖所示,overflowed表示全連接隊列溢出的次數(shù),sockets dropped表示半連接隊列溢出的次數(shù)。
-
ss命令,執(zhí)行ss -lnt
上面看到Send-Q 表示第三列的listen端口上的全連接隊列最大為5,第一列Recv-Q為全連接隊列當(dāng)前使用了多少。
接著我們看看怎么設(shè)置全連接、半連接隊列大小吧:
全連接隊列的大小取決于min(backlog, somaxconn)。backlog是在socket創(chuàng)建的時候傳入的,somaxconn是一個os級別的系統(tǒng)參數(shù)。而半連接隊列的大小取決于max(64, /proc/sys/net/ipv4/tcp_max_syn_backlog)。
在日常開發(fā)中,我們往往使用servlet容器作為服務(wù)端,所以我們有時候也需要關(guān)注容器的連接隊列大小。在tomcat中backlog叫做acceptCount,在jetty里面則是acceptQueueSize。
RST異常
RST包表示連接重置,用于關(guān)閉一些無用的連接,通常表示異常關(guān)閉,區(qū)別于四次揮手。
在實際開發(fā)中,我們往往會看到connection reset / connection reset by peer錯誤,這種情況就是RST包導(dǎo)致的。
1、端口不存在
如果像不存在的端口發(fā)出建立連接SYN請求,那么服務(wù)端發(fā)現(xiàn)自己并沒有這個端口則會直接返回一個RST報文,用于中斷連接。
2、主動代替FIN終止連接
一般來說,正常的連接關(guān)閉都是需要通過FIN報文實現(xiàn),然而我們也可以用RST報文來代替FIN,表示直接終止連接。實際開發(fā)中,可設(shè)置SO_LINGER數(shù)值來控制,這種往往是故意的,來跳過TIMED_WAIT,提供交互效率,不閑就慎用。
3、客戶端或服務(wù)端有一邊發(fā)生了異常,該方向?qū)Χ税l(fā)送RST以告知關(guān)閉連接
我們上面講的tcp隊列溢出發(fā)送RST包其實也是屬于這一種。這種往往是由于某些原因,一方無法再能正常處理請求連接了(比如程序崩了,隊列滿了),從而告知另一方關(guān)閉連接。
4、接收到的TCP報文不在已知的TCP連接內(nèi)
比如,一方機(jī)器由于網(wǎng)絡(luò)實在太差TCP報文失蹤了,另一方關(guān)閉了該連接,然后過了許久收到了之前失蹤的TCP報文,但由于對應(yīng)的TCP連接已不存在,那么會直接發(fā)一個RST包以便開啟新的連接。
5、一方長期未收到另一方的確認(rèn)報文,在一定時間或重傳次數(shù)后發(fā)出RST報文
這種大多也和網(wǎng)絡(luò)環(huán)境相關(guān)了,網(wǎng)絡(luò)環(huán)境差可能會導(dǎo)致更多的RST報文。
之前說過RST報文多會導(dǎo)致程序報錯,在一個已關(guān)閉的連接上讀操作會報connection reset,而在一個已關(guān)閉的連接上寫操作則會報connection reset by peer。通常我們可能還會看到broken pipe錯誤,這是管道層面的錯誤,表示對已關(guān)閉的管道進(jìn)行讀寫,往往是在收到RST,報出connection reset錯后繼續(xù)讀寫數(shù)據(jù)報的錯,這個在glibc源碼注釋中也有介紹。
我們在排查故障時候怎么確定有RST包的存在呢?當(dāng)然是使用tcpdump命令進(jìn)行抓包,并使用wireshark進(jìn)行簡單分析了。tcpdump -i en0 tcp -w xxx.cap,en0表示監(jiān)聽的網(wǎng)卡。
接下來我們通過wireshark打開抓到的包,可能就能看到如下圖所示,紅色的就表示RST包了。
TIME_WAIT和CLOSE_WAIT
TIME_WAIT和CLOSE_WAIT是啥意思相信大家都知道。
在線上時,我們可以直接用命令netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'來查看time-wait和close_wait的數(shù)量
用ss命令會更快ss -ant | awk '{++S[$1]} END {for(a in S) print a, S[a]}'
1、TIME_WAIT
time_wait的存在一是為了丟失的數(shù)據(jù)包被后面連接復(fù)用,二是為了在2MSL的時間范圍內(nèi)正常關(guān)閉連接。它的存在其實會大大減少RST包的出現(xiàn)。
過多的time_wait在短連接頻繁的場景比較容易出現(xiàn)。這種情況可以在服務(wù)端做一些內(nèi)核參數(shù)調(diào)優(yōu):
#表示開啟重用。允許將TIME-WAIT sockets重新用于新的TCP連接,默認(rèn)為0,表示關(guān)閉
net.ipv4.tcp_tw_reuse = 1
#表示開啟TCP連接中TIME-WAIT sockets的快速回收,默認(rèn)為0,表示關(guān)閉
net.ipv4.tcp_tw_recycle = 1
當(dāng)然我們不要忘記在NAT環(huán)境下因為時間戳錯亂導(dǎo)致數(shù)據(jù)包被拒絕的坑了,另外的辦法就是改小tcp_max_tw_buckets,超過這個數(shù)的time_wait都會被干掉,不過這也會導(dǎo)致報time wait bucket table overflow的錯。
2、CLOSE_WAIT
close_wait往往都是因為應(yīng)用程序?qū)懙挠袉栴},沒有在ACK后再次發(fā)起FIN報文。close_wait出現(xiàn)的概率甚至比time_wait要更高,后果也更嚴(yán)重。往往是由于某個地方阻塞住了,沒有正常關(guān)閉連接,從而漸漸地消耗完所有的線程。
想要定位這類問題,最好是通過jstack來分析線程堆棧來排查問題,這里僅舉一個例子。
開發(fā)同學(xué)說應(yīng)用上線后CLOSE_WAIT就一直增多,直到掛掉為止,jstack后找到比較可疑的堆棧是大部分線程都卡在了countdownlatch.await方法,找開發(fā)同學(xué)了解后得知使用了多線程但是卻沒有catch異常,修改后發(fā)現(xiàn)異常僅僅是最簡單的升級sdk后常出現(xiàn)的class not found。
總結(jié)
以上是生活随笔為你收集整理的线上问题定位------网络瓶颈的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux进程的高级管理,sched_y
- 下一篇: vue+element UI仿携程购票页