不要再說你不會了——網路效能問題排查思路

藍胖子的程式設計夢發表於2023-04-13

網路效能問題排查思路

服務監控系列文章

服務監控系列影片

網路問題往往是效能排查中最複雜的一個問題,因為網路問題往往涉及的鏈路比較長,排查起來不僅僅是看本地機器的指標就可以了。本文將展示一個比較系統的排查網路問題的思路。

我們往往都是透過類似prometheus,grafana搭建的監控平臺對機器的各項指標進行監控,其中就包括網路效能,當發現指標異常後,你就需要定位到具體的程式了,定位到程式還沒完,你還需要定位到程式中是哪段程式碼引發的這個問題。整個過程你可以看到是一個大範圍到小範圍逐步定位的過程,大致分為3個步驟:

1,系統層面發現問題

2,定位到具體異常程式

3,定位到程式中引發異常的程式碼段

你就像偵探一樣,一層層抽絲剝繭,排查過程十分有趣。

接下來,我們來挨個看看,具體每個步驟,我們應該如何做?

從系統層面發現網路問題

衡量網路效能,往往我們會根據幾個重要的指標,來挨個看看。

首先是 網路卡流入流出的流量 ,衡量流量大小也是有幾種單位方式,MBS代表每秒多少個MB位元組,Mbps代表每秒多少個M位元位,他們的換算關係是 MBS = Mbps / 8。

接著一個指標是 每秒收發包的數量 英文是pps,一個完整的網路請求是其實是分成很多個網路包進行傳送的,在描述網路卡效能時,一般都是將 流入流出流量大小和pps大小結合起來闡述。

然後是另一個指標 丟包數 當產生丟包行為時,可能會因為tcp的重傳機制,讓丟了的包重新傳送到對端,但是重傳必然會導致網路延遲的增大,當丟包數急劇上升時,我們也認為這是網路的一個異常表現。

丟包數或者丟包率指標我認為 是 pps和流入流出的流量 功能 不太一樣的指標型別,pps和流入流出的流量我把它們稱作流量型別的網路指標,透過它們,能夠知道網路流量的大小,而丟包數 是則是形容網路傳輸的好壞。

好了,說完了幾個指標,再談談如何用上系統工具來實際的觀察下這些指標值,不然與紙上談兵又有何異。

可以使用sar命令 每1秒統計一次網路介面的發包數以及進出流量,連續顯示5次。

sar -n DEV 1 5
03:05:31 PM     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s
03:05:32 PM      eth0      3.00      1.00      0.26      0.20      0.00      0.00      0.00
03:05:32 PM        lo    138.00    138.00     40.56     40.56      0.00      0.00      0.00

IFACE:網路介面名稱。

rxpck/s、txpck/s:每秒收或發的資料包數量。

rxkB/s、txkB/s:每秒收或發的位元組數,以kB/s為單位。

rxcmp/s、txcmp/s:每秒收或發的壓縮過的資料包數量。

rxmcst/s:每秒收到的多播資料包。

sar不能看到 丟包數的指標資訊 ,因為丟包問題涉及的鏈路較長,我將放在此文的後面專門闡述。但是起碼sar能夠看pps以及進出流量了。不過這個只是從系統層面宏觀的看網路情況,當你發現系統層面流量異常後,如何定位到是哪個程式導致流量異常的呢?

從程式角度看網路情況

接下來,我們從程式的程式來看網路情況。觀察的內容還是和系統層面差不多,不外乎就是進出流量,傳送資料包等等。我將介紹兩種工具,對比起系統層面看網路情況,它們能從更低的維度看網路情況。

首先是iftop 工具,它能找出是哪個ip消耗流量最多。

└─────────────────────┴─────────────────────┴──────────────────────┴─────────────────────┴──────────────────────
hw-sg1-test-0001                           => 192.168.0.94                               1.93Kb  3.35Kb  3.35Kb
                                           <=                                            34.1Kb  99.1Kb  99.1Kb
hw-sg1-test-0001                           => 192.168.0.133                              3.36Kb  4.68Kb  4.68Kb
                                           <=                                            17.5Kb  93.3Kb  93.3Kb
hw-sg1-test-0001                           => 192.168.0.233                              6.07Kb  5.03Kb  5.03Kb
                                           <=                                             108Kb  90.3Kb  90.3Kb
hw-sg1-test-0001                           => 192.168.0.119                                 0b    800b    800b
                                           <=                                               0b   14.1Kb  14.1Kb
hw-sg1-test-0001                           => 47.74.128.115                              5.17Kb  1.72Kb  1.72Kb
                                           <=                                            25.5Kb  8.50Kb  8.50Kb
hw-sg1-test-0001                           => 192.168.0.95                                  0b    604b    604b
                                           <=                                               0b   7.41Kb  7.41Kb
hw-sg1-test-0001                           => ec2-3-120-42-58.eu-central-1.compute.amaz  1.63Kb  2.05Kb  2.05Kb
                                           <=                                               0b   5.45Kb  5.45Kb
hw-sg1-test-0001                           => 183.6.45.216                               4.94Kb  5.05Kb  5.05Kb
                                           <=                                            1.08Kb   920b    920b
hw-sg1-test-0001                           => 192.168.0.107                              2.48Kb   848b    848b
                                           <=                                            12.1Kb  4.05Kb  4.05Kb
hw-sg1-test-0001                           => 100.125.128.250                             904b   1.41Kb  1.41Kb
                                           <=                                            1.44Kb  2.50Kb  2.50Kb
hw-sg1-test-0001                           => 47.74.249.92                                  0b     53b     53b
                                           <=                                               0b     61b     61b
hw-sg1-test-0001                           => 192.168.0.170                                 0b     53b     53b
                                           <=                                               0b     53b     53b
hw-sg1-test-0001                           => 172.21.2.153                                  0b     69b     69b
                                           <=                                               0b      0b      0b
────────────────────────────────────────────────────────────────────────────────────────────────────────────────
TX:             cum:   19.3KB   peak:   30.1Kb                                  rates:   26.5Kb  25.7Kb  25.7Kb
RX:                     244KB            391Kb                                            199Kb   326Kb   326Kb
TOTAL:                  264KB            417Kb                                            226Kb   351Kb   351Kb

=> 和<= 代表網路包的流向,每一行右邊有3列變化的值,分別是在2s內,10s內,40s內的平均每秒流量的大小,而在iftop輸出的最下面,則是系統整體的網路流量情況了。但是這樣只能看ip,如果定位到程式呢?iftop可以根據目的地址和源地址聚合流量,當進入iftop輸出介面時,按s就是按源地址聚合流量,按d則是按目的地址聚合流量。 預設iftop輸出介面時不顯示埠的,但如果要按埠維度對流量進行區分,需要在iftop輸出介面,按S或者D,S和D分別代表按源地址的埠對流量進行劃分,D代表按目的地址埠對流量進行劃分。

當在iftop介面 分別按S和d後,將會得到主機上按埠維度進行區分的流量情況。如下所示:

hw-sg1-test-0001:wap-wsp                   =>  *                                         9.92Kb  10.5Kb  11.4Kb
                                           <=                                             161Kb   117Kb   171Kb
hw-sg1-test-0001:64340                     =>  *                                            0b    811b    608b
                                           <=                                               0b   39.1Kb  29.4Kb
hw-sg1-test-0001:51338                     =>  *                                            0b    810b    624b
                                           <=                                               0b   38.7Kb  29.0Kb

這樣便能看到主機上是哪個埠占用的流程最高了,有了埠就能很好的定位到程式了。

關於iftop一些高階用法可以通道man iftop去檢視,這裡就不再展開了

對比起iftop而言,有個更簡單幫助我們檢視程式上網路情況的工具,叫做nethogs。

檢視程式佔用頻寬情況 ,命令如下:

sudo nethogs eth0

image.png

檢視nethogs 的輸出,每一行最後則是程式瞬時每秒的傳送與接收的流量。透過nethogs,我們可以直接定位到是哪個程式有流量異常產生。

如何定位到具體程式碼

透過iftop和nethogs 命令,我們可以定位是哪個程式產生的流量異常了,但是如何定位到具體的程式碼呢?

由於我比較熟悉golang,這裡介紹下用go開發的程式,可以怎麼找出流量異常的程式碼,golang自帶有分析網路延遲的工具 go trace可以檢視網路排程延遲。 如果是go開發的程式 發生流量異常,相信透過go trace的grapgh能找到 網路延遲最高的一部分程式碼,定位到問題。關於go trace的原理以及使用,我在golang pprof 監控系列(1) —— go trace 統計原理與使用的詳細的介紹。

有了上述的排查思路,相信在面對流量異常的情況,我們能很快的找到具體是哪段程式碼引發的異常了。接下來我們來談談如何排查丟包問題。

如何查詢丟包問題

tcp ip模型已經成為事實標準,網路包會經過tcp,ip模型的幾個層次,傳送和接收的每一個步驟都有可能產生丟包。排查的過程則是檢查每一個步驟是否有丟包行為產生。

首先來看下應用層丟包如何排查。

應用層

核心在監聽套接字的時候,在三次握手時,會建立兩個佇列,在伺服器收到syn 包時,會建立半連線佇列,並將這條握手未完成的連線 放到裡面,然後回覆ack,syn包給客戶端,當客戶端回覆ack時,核心會將這條連線放到全連線佇列裡,呼叫accept就是將連線從全連線佇列裡取出。

如果半連線佇列或者全連線佇列滿了,則可能發生丟包行為。

半連線佇列

半連線佇列大小由核心引數tcp_max_syn_backlog定義。

sysctl -w net.ipv4.tcp_max_syn_backlog=1024

另外,上述行為受到核心引數tcp_syncookies的影響,若啟用syncookie機制,當半連線佇列溢位時,並不會直接丟棄SYN包,而是回覆帶有syncookie的SYN+ACK包,設計的目的是防範SYN Flood造成正常請求服務不可用。

sysctl -w net.ipv4.tcp_syncookies=1

如何確認是半連線導致的丟包呢,我們可以透過下面的命令。

dmesg | grep "TCP: drop open request from"

dmesg可以檢視核心的輸出日誌,如果半連線有丟包產生,那麼dmesg會看到對應的輸出。

半連線佇列的連線數量也可以透過netstat命令統計SYN_RECV狀態的連線得知。因為在3次握手時,收到syn包的連線的狀態是SYN_RECV狀態,而整個狀態的持續時間是很短的,如果用netstat發現SYN_RECV狀態的連線非常多,則說明半連線佇列可能滿了。

netstat -ant|grep SYN_RECV|wc -l

接著我們來看下分析全連線佇列相關的命令。

全連線佇列

ss 命令可以檢視全連線佇列大小

# -l 顯示正在監聽 
# -n 不解析服務名稱
# -t 只顯示 tcp socket
$ ss -lnt
State       Recv-Q Send-Q                     Local Address:Port                                    Peer Address:Port              
LISTEN      0      50                                     *:2181                                               *:*                  
LISTEN      0      32768                          127.0.0.1:9200                                               *:*                  
LISTEN      0      32768                      192.168.0.233:9200                                               *:* 

Recv-Q:當前全連線佇列的大小,也就是當前已完成三次握手並等待服務端 accept() 的 TCP 連線;
Send-Q:當前全連線最大佇列長度,上面的輸出結果說明監聽 8088 埠的 TCP 服務,最大全連線長度為 128;
listen 的全連線大小可以在listen系統呼叫的時候指定go原始碼裡讀取的是 /proc/sys/net/core/somaxconn 裡的值。

# -n 不解析服務名稱
# -t 只顯示tcp socket 
$ ss -nt
State       Recv-Q Send-Q                     Local Address:Port                                    Peer Address:Port              
ESTAB       0      0                          192.168.0.233:27266                                  192.168.0.129:3306               
ESTAB       0      0                          192.168.0.233:30212                                  192.168.0.129:3306               
ESTAB       0      0                          192.168.0.233:8000                                   100.125.64.81:44948  

Recv-Q:已收到但未被應用程式讀取的位元組數;
Send-Q:已傳送但未收到確認的位元組數;

當全連線佇列滿了後,預設核心會將包丟棄,但是也可以指定其他策略。

cat /proc/sys/net/ipv4/tcp_abort_on_overflow
0

0 :如果全連線佇列滿了,那麼 server 扔掉 client 發過來的 ack ;
1 :如果全連線佇列滿了,server 傳送一個 reset 包給 client,表示廢掉這個握手過程和這個連線;

透過檢視應用層的全連線佇列的相關指標,相信你能夠定位 那些由於應用程式處理包緩慢導致的丟包場景了,因為應用程式處理包緩慢,持續小於 網路卡的接包速度的話,將導致全連線佇列很快就滿了,導致丟包。

接著我們來看下傳輸層,網路層的丟包場景。

傳輸層,網路層

之所以把這兩者放到一起進行講解,因為很多工具的分析都橫跨了這兩個層次。

防火牆導致的丟包

先來看下其中的防火牆導致丟包的情況,除了防火牆本身配置DROP規則導致丟包外,與防火牆有關的還有連線跟蹤表nf_conntrack,Linux為每個經過核心網路棧的資料包,生成一個新的連線記錄項,當伺服器處理的連線過多時,連線跟蹤表被打滿,伺服器會丟棄新建連線的資料包。

以下命令可以檢視nf_conntrack表最大連線數

$ cat /proc/sys/net/netfilter/nf_conntrack_max
65536
# 檢視nf_conntrack表當前連線數
$ cat /proc/sys/net/netfilter/nf_conntrack_count
7611

tcp丟包以及原因計數

用nstat命令 檢視網路協議層丟包情況以及丟包原因,命令如下。

[webserver@hw-sg1-test-0001 ~]$ nstat -az | grep -i drop
TcpExtLockDroppedIcmps          0                  0.0
TcpExtListenDrops               939                0.0
TcpExtTCPPrequeueDropped        0                  0.0
TcpExtTCPBacklogDrop            10684              0.0
TcpExtTCPMinTTLDrop             0                  0.0
TcpExtTCPDeferAcceptDrop        0                  0.0
TcpExtTCPReqQFullDrop           0                  0.0
TcpExtTCPOFODrop                0                  0.0

其實netstat -s命令也可以看tcp層的丟包情況,不過我還是強烈建議將 它換為nstat 命令因為nstat效率更高,並且統計的內容命名也更符合rfc文件規範。

nstat 除了列出丟包的情況,還有其他統計資訊,具體每個指標的含義可以參考 這個文件

此外,網路層 mtu的設定有時可能導致丟包的產生,如果傳送的mtu包的大小超過網路卡規定的大小,並且網路卡不允許分片,那麼則會產生丟包。

接著就是鏈路層丟包的場景了。

鏈路層

鏈路層是物理網路卡丟包的場景了,統計鏈路層丟包的資料可以用netstat得到。
netstat 可以統計網路卡環形緩衝區溢位(RX-OVR,TX-OVR),以及網路卡發生錯誤(RX-ERR,TX-ERR)的次數。

root@nginx:/# netstat -i
Kernel Interface table
Iface      MTU    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0       100      157      0    344 0            94      0      0      0 BMRU
lo       65536        0      0      0 0             0      0      0      0 LRU

相關文章