Linux TCP參數調優

在寫這篇文章之前寫過一篇文章:tcp too many orphaned sockets 問題引發的思考,裏面講了主要講了下端口,socket相關也涉及到部分參數調優,但是最近又遇到一些網絡方面的問題涉及到一些調參,所以我覺得應該單獨整理一片關於TCP調優的博客,也不會顯得那麼冗餘和雜亂了。

(下文中涉及到對/etc/sysctl.conf中參數的修改都是永久修改,後文 不再贅述。方法:將參數添加到/etc/sysctl.conf中,然後執行sysctl -p使參數生效,永久生效)

0x01 端口相關

關於端口耗盡的問題,之前那篇博客也有講到,這裏就不囉嗦了。一般作爲服務端,都是監聽一個端口,然後for循環去accept連接,然後進行處理就行。因此一般也不存在服務端端口被耗盡。
比如golang中這樣的代碼:

//以省略不相關代碼
func main() {
    l, err := net.Listen("tcp", ":8888")
    if err != nil {
        fmt.Println("listen error:", err)
        return
    }

    for {
        c, err := l.Accept()
        if err != nil {
            fmt.Println("accept error:", err)
            break
        }
        // start a new goroutine to handle
        // the new connection.
        go handleConn(c)
    }
}

當然服務器上一般不會只有一個服務,可能還有其他服務,或者你的服務收到請求後還會作爲client去調用其他service,這個時候就需要關注機器的端口範圍了。
調優點一:
默認端口範圍

# sysctl -a|grep ip_local_port_range
net.ipv4.ip_local_port_range = 32768	60999

可以視情況調整,比如:

net.ipv4.ip_local_port_range = 1024 65535

0x02 資源相關

accpet調用成功之後就成功建立了一個連接了,java中直接返回一個socket,golang中還封裝了一下返回一個net.Conn,但是最終結果都是一樣的:通過持有這個socket進行讀寫,socket的本質是一個文件描述符這個在之前的博客中也講到了。

//golang 的net.Conn的 Read Write 函數:
// Read implements the Conn Read method.
func (c *conn) Read(b []byte) (int, error) {
    if !c.ok() {
        return 0, syscall.EINVAL
    }
    n, err := c.fd.Read(b)
    if err != nil && err != io.EOF {
        err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
    }
    return n, err
}

// Write implements the Conn Write method.
func (c *conn) Write(b []byte) (int, error) {
    if !c.ok() {
        return 0, syscall.EINVAL
    }
    n, err := c.fd.Write(b)
    if err != nil {
        err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
    }
    return n, err
}

上面廢話這麼多就是想重點說明:socket作爲一種文件資源,就會收到Linux系統的限制,你不能隨意無限使用。這個限制體現在:Linux對文件描述符資源的限制。
調優點二:
可調優參數有:

  1. 用戶級別
    查看Linux系統用戶最大打開的文件限制
#ulimit -n 
65535

修改打開文件限制
vim /etc/security/limits.conf

root soft nofile 102400
root hard nofile 102400

其中root指定了要修改哪個用戶的打開文件數限制。
可用’*'號表示修改所有用戶的限制;soft或hard指定要修改軟限制還是硬限制;
102400則指定了想要修改的新的限制值,即最大打開文件數(請注意軟限制值要小於或等於硬限制)

  1. 系統級別
    調優點三:
    查看Linux系統對同時打開文件數的硬限制:
# sysctl -a|grep file-max
fs.file-max = 65535

這個限制是指所有用戶的總和,即整個系統支持的最大值
修改file-max限制,/etc/sysctl.conf文件中

fs.file-max = 6815744

一般好點的機器上這個值都還是比較大的。

上面說的了Linux對文件資源的限制,此外大家知道TCP在收發信息的時候是有一個緩衝區的,而緩衝區又是一種內存資源,所有Linux系統肯定也要對該資源進行限制。

調優點四:

  1. 緩衝區調優
    設置TCPTCP數據接收、發送窗口大小
net.core.optmem_max = 513920
net.core.rmem_default = 256960
net.core.rmem_max = 513920
net.core.wmem_default = 256960
net.core.wmem_max = 513920

還有這兩個參數

net.ipv4.tcp_rmem = 8760  256960  4088000
net.ipv4.tcp_wmem = 8760  256960  4088000

其中第一個值是爲socket接收緩衝區分配的最少字節數;第二個值是默認值(該值會被rmem_default覆 蓋),緩衝區在系統負載不重的情況下可以增長到這個值;第三個值是接收緩衝區空間的最大字節數(該值會被rmem_max覆蓋)。

  1. TCP可使用總內存
    這個參數在之前的博文中也有提到,其意義是限制系統中所有TCP可使用的內存總量
net.ipv4.tcp_mem = 131072  262144  524288

第一個值是內存使用的下限;第二個值是內存壓力模式開始對緩衝區使用應用壓力 的上限;第三個值是內存使用的上限。在這個層次上可以將報文丟棄,從而減少對內存的使用。對於較大的BDP可以增大這些值(注意,其單位是內存頁而不是字節)。

上述參數中可以視情況調更大些。

0x03 防火牆相關

線上Hbase集羣在訪問量突增的時候發現短時間部分請求失敗,在一臺機器上dmesg發現錯誤日誌如下:

nf_conntrack: table full, dropping packet

這是因爲開啓了防火牆,防火牆會對每個連接進行追蹤,這個數量是有限制的如果超過了就丟包了。因此需要調大該參數,如下:
調優點四:

net.netfilter.nf_conntrack_max=1048576
net.nf_conntrack_max=1048576

0x04 比較隱晦的幾個參數

調優點五

net.core.netdev_max_backlog = 400000
net.core.somaxconn = 100000

這兩個參數對服務的影響還是挺重要的,如果用nginx做反向代理,如果突然流量暴增,這個時候nginx可能會返回大量502,我覺得其中的一個原因就是這兩個參數配置過低導致的。
這兩個參數一般在機器上設置的比較小,尤其需要引起注意。比如你可能看到的默認值是這樣的:

# sysctl -a|grep netdev_max
net.core.netdev_max_backlog = 1000
# sysctl -a|grep somaxconn
net.core.somaxconn = 128

這兩個參數是什麼意思呢?
netdev_max_backlog對應的是TCP半連接隊列大小,對應圖中sync queue
somaxconn對應的是TCP的全連接隊列大小,對應圖中的accept queue
TCP三次握手中,在第一步server收到client的syn後,就會把相關信息放到半連接隊列(sync queue)中,同時回覆syn+ack給client(第二步)

(sync flood 攻擊也是利用這個弱點,直接打滿你的sync queu,這個時候其他請求的連接也進不來了。)

第三步的時候server收到client的ack,如果這時全連接隊列沒滿,那麼從半連接隊列拿出相關信息放入到全連接隊列中,否則按tcp_abort_on_overflow指示的執行。

這裏又涉及到兩個參數:
調優點六:

net.ipv4.tcp_abort_on_overflow=0
net.ipv4.tcp_synack_retries = 2

tcp_abort_on_overflow 爲0表示如果三次握手第三步的時候全連接隊列滿了那麼server扔掉client 發過來的ack(在server端認爲連接還沒建立起來)
但是server會過一段時間再次發送syn+ack給client(也就是重新走握手的第二步),如果client超時等待比較短,就很容易異常了。如果tcp_abort_on_overflow的值爲1的話,那麼就會直接rst這個連接。

此外,對於一個TCP連接來說,其半連接隊列和全連接隊列不是直接使用netdev_max_backlog和somaxconn的值,而是如下方式:
全連接隊列的大小取決於:min(backlog, somaxconn),backlog是你寫代碼的時候創建socket的時候傳入的
半連接隊列的大小取決於:max(64, /proc/sys/net/ipv4/tcp_max_syn_backlog)。 不同版本的os會有些差異

0x05連接回收

如果機器上出現大量FIN_WAIT2, TIME_WAIT等狀態那就應該考慮調下面幾個參數了
調優點七:

net.ipv4.tcp_syncookies = 1 
net.ipv4.tcp_tw_reuse = 1 
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30

TIME_WAIT狀態將持續2個MSL(Max Segment Lifetime),在Windows下默認爲4分鐘,即240秒。一般情況下TIME_WAIT狀態下的socket不能被回收使用,因此需要優化上述參數,避免端口占用。

那如果是CLOSE_WAIT狀態呢?
這個時候應該看看自己代碼是不是有問題了,這個狀態是被動關閉方在收到FIN報文後但沒有向主動關閉方發送FIN的狀態。

注:上面涉及到的所有調優點都應該根據自己的運行環境來選擇適當的值,最好先經過測試環境的驗證在放到線上

參考:

https://blog.csdn.net/Just_shunjian/article/details/78288229
https://testerhome.com/topics/7509
關於somaxconn, 阿里中間件的博客:http://jm.taobao.org/2017/05/25/525-1/
https://blog.csdn.net/erlib/article/details/50236919
https://colobu.com/2014/09/18/linux-tcpip-tuning/
golang tcp 編程:https://tonybai.com/2015/11/17/tcp-programming-in-golang/
https://cnblogs.com/pangguoping/p/5830328.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章