百萬並發之 tcp_mem
在服務端,連接達到一定數量,諸如50W時,有些隱藏很深的問題,就不斷的拋出來。 通過查看dmesg命令查看,發現大量TCP: too many of orphaned sockets錯誤,也很正常,下面到了需要調整tcp socket參數的時候了。
第一個需要調整的是tcp_rmem,即TCP讀取緩沖區,單位為字節,查看默認值
cat /proc/sys/net/ipv4/tcp_rmem
4096 87380 4161536
默認值為87380 byte ≈ 86K,最小為4096 byte=4K,最大值為4064K。
第二個需要調整的是tcp_wmem,發送緩沖區,單位是字節,默認值
cat /proc/sys/net/ipv4/tcp_wmem
4096 16384 4161536
解釋同上
第三個需要調整的tcp_mem,調整TCP的內存大小,其單位是頁,1頁等於4096字節。系統默認值:
cat /proc/sys/net/ipv4/tcp_mem
932448 1243264 1864896
tcp_mem(3個INTEGER變量):low, pressure, high
low:當TCP使用了低於該值的內存頁面數時,TCP不會考慮釋放內存。
pressure:當TCP使用了超過該值的內存頁面數量時,TCP試圖穩定其內存使用,進入pressure模式,當內存消耗低於low值時則退出pressure狀態。
high:允許所有tcp sockets用於排隊緩沖數據報的頁面量,當內存占用超過此值,系統拒絕分配socket,後臺日誌輸出“TCP: too many of orphaned sockets”。
一般情況下這些值是在系統啟動時根據系統內存數量計算得到的。 根據當前tcp_mem最大內存頁面數是1864896,當內存為(1864896*4)/1024K=7284.75M時,系統將無法為新的socket連接分配內存,即TCP連接將被拒絕。
實際測試環境中,據觀察大概在99萬個連接左右的時候(零頭不算),進程被殺死,觸發out of socket memory錯誤(dmesg命令查看獲得)。每一個連接大致占用7.5K內存(下面給出計算方式),大致可算的此時內存占用情況(990000 * 7.5 / 1024K = 7251M)。
這樣和tcp_mem最大頁面值數量比較吻合,因此此值也需要修改。
三個TCP調整語句為:
echo “net.ipv4.tcp_mem = 786432 2097152 3145728”>> /etc/sysctl.conf
echo “net.ipv4.tcp_rmem = 4096 4096 16777216”>> /etc/sysctl.conf
echo “net.ipv4.tcp_wmem = 4096 4096 16777216”>> /etc/sysctl.conf
備註: 為了節省內存,設置tcp讀、寫緩沖區都為4K大小,tcp_mem三個值分別為3G 8G 16G,tcp_rmem和tcp_wmem最大值也是16G。
目標達成
經過若幹次的嘗試,最終達到目標,1024000個持久連接。1024000數字是怎麽得來的呢,兩臺物理機器各自發出64000個請求,兩個配置為6G左右的centos測試端機器(綁定7個橋接或NAT連接)各自發出640007 = 448000。也就是 1024000 = (64000) + (64000) + (640007) + (64000*7), 共使用了16個網卡(物理網卡+虛擬網卡)。
終端輸出
…
online user 1023990
online user 1023991
online user 1023992
online user 1023993
online user 1023994
online user 1023995
online user 1023996
online user 1023997
online user 1023998
online user 1023999
online user 1024000
在線用戶目標達到1024000個!
服務器狀態信息
服務啟動時內存占用:
total used free shared buffers cached
Mem: 10442 271 10171 0 22 78
-/+ buffers/cache: 171 10271
Swap: 8127 0 8127
系統達到1024000個連接後的內存情況(執行三次 free -m 命令,獲取三次結果):
total used free shared buffers cached
Mem: 10442 7781 2661 0 22 78
-/+ buffers/cache: 7680 2762
Swap: 8127 0 8127
total used free shared buffers cached
Mem: 10442 7793 2649 0 22 78
-/+ buffers/cache: 7692 2750
Swap: 8127 0 8127
total used free shared buffers cached
Mem: 10442 7804 2638 0 22 79
-/+ buffers/cache: 7702 2740
Swap: 8127 0 8127
這三次內存使用分別是7680,7692,7702,這次不取平均值,取一個中等偏上的值,定為7701M。那麽程序接收1024000個連接,共消耗了 7701M-171M = 7530M內存, 7530M*1024K / 1024000 = 7.53K, 每一個連接消耗內存在為7.5K左右,這和在連接達到512000時所計算較為吻合。
虛擬機運行Centos內存占用,不太穩定,但一般相差不大,以上數值,僅供參考。
執行top -p 某刻輸出信息:
top - 17:23:17 up 18 min, 4 users, load average: 0.33, 0.12, 0.11
Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
Mem: 10693580k total, 6479980k used, 4213600k free, 22916k buffers
Swap: 8323056k total, 0k used, 8323056k free, 80360k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
2924 yongboy 20 0 82776 74m 508 R 51.3 0.7 3:53.95 server
執行vmstate:
vmstat
procs -----------memory---------- —swap-- -----io---- --system-- -----cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 2725572 23008 80360 0 0 21 2 1012 894 0 9 89 2 0
獲取當前socket連接狀態統計信息:
cat /proc/net/sockstat
sockets: used 1024380
TCP: inuse 1024009 orphan 0 tw 0 alloc 1024014 mem 2
UDP: inuse 11 mem 1
UDPLITE: inuse 0
RAW: inuse 0
FRAG: inuse 0 memory 0
UDPLITE: inuse 0
獲取當前系統打開的文件句柄:
sysctl -a | grep file
fs.file-nr = 1025216 0 1048576
fs.file-max = 1048576
此時任何類似於下面查詢操作都是一個慢,等待若幹時間還不見得執行完畢。
netstat -nat|grep -i “8000”|grep ESTABLISHED|wc -l
netstat -n | grep -i “8000” | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}‘
以上兩個命令在二三十分鐘過去了,還未執行完畢,只好停止。
小結
本次從頭到尾的測試,所需要有的linux系統需要調整的參數也就是那麽幾個,匯總一下:
echo “* - nofile 1048576” >> /etc/security/limits.conf
echo “fs.file-max = 1048576” >> /etc/sysctl.conf
echo “net.ipv4.ip_local_port_range = 1024 65535” >> /etc/sysctl.conf
echo “net.ipv4.tcp_mem = 786432 2097152 3145728” >> /etc/sysctl.conf
echo “net.ipv4.tcp_rmem = 4096 4096 16777216” >> /etc/sysctl.conf
echo “net.ipv4.tcp_wmem = 4096 4096 16777216” >> /etc/sysctl.conf
其它沒有調整的參數,僅僅因為它們暫時對本次測試沒有帶來什麽影響,實際環境中需要結合需要調整類似於SO_KEEPALIVE、tcpmax_orphans等大量參數。
tcp_mem:单位是page,此值是动态的,linux根据机器自身内存情况进行分配
可根据张小三资源网centos性能优化那篇文章里查看查询方式。
最小:在这个页数之下,TCP不担心它的内存需求。
压力:当TCP分配的内存量超过此页数时,TCP将减缓其内存消耗并进入内存压力模式,当内存消耗低于“min”时,该模式将退出。
最大:所有TCP套接字允许排队的页面数。超出则打印Out of socket memory
假设一个page大小是4K
net.ipv4.tcp_mem = 196608 262144 393216
那么对应的内存分配就是:768M 1G 1.57G
内存为1G时进入压力模式
大于TCP套接字允许排队的页面数则打印。
tcp_rmem:单位是字节 接收缓存区大小,缓存从对端接收的数据,后续会被应用程序读取
最小:默认值4K,TCP套接字使用的接收缓冲区的最小大小。即使在中等的内存压力下,它也能保证连接到每个TCP套接字。
默认:默认值87380字节,TCP套接字使用的接收缓冲区的初始大小。此值覆盖net.core.rmem默认值。此值将导致窗口为65535,默认设置为tcp_adv_win_scale和tcp_app_win:0,默认设置为tcp_app_win时,窗口会小一些。
最大:介于87380字节和6MB之间,取决RAM大小。TCP套接字接收器允许接收缓冲区的最大大小,此值不会覆盖net.core.rmem_max。使用SO_RCVBUF调用setsockopt()将禁用该套接字的接收缓冲区大小的自动调整,在这种情况下,将忽略此值。
tcp_wmem:单位字节 发送缓存区大小,缓存应用程序的数据,有序列号被应答确认的数据会从发送缓冲区删除掉。
最小:默认4K,为TCP套接字的发送缓冲区保留的内存量。每个TCP套接字由于其诞生的事实而有权使用它。
默认:最大16K自动调整。TCP套接字使用的发送缓冲区的初始大小。此值将覆盖net.core.wmem_default。通常低于net.core.wmem_default。
最大:介于64K和4MB之间,具体取决于RAM大小。TCP套接字的发送缓冲区所允许的最大内存量。此值不会覆盖net.core.wmem_max。使用SO_SNDBUF调用setsockopt()会禁用该套接字的发送缓冲区大小的自动调整,在这种情况下该值将被忽略。