實(shí)戰(zhàn)單機(jī)50萬Tcp連接
本來準(zhǔn)備嘗試挑戰(zhàn)百萬鏈接, 但是申請(qǐng)不到機(jī)器資源, 只好在自己的筆記本上來跑測(cè)試. 機(jī)器配置如下:
Memory: 11.4 GiB
Intel? Core? i5-5200U CPU @ 2.20GHz × 4
技術(shù)框架選擇的是Vert.x, 這是比Netty更好的服務(wù)端框架. 連續(xù)測(cè)試幾天, 最終壓到了48萬, 過程中也遇到了很多問題(小馬過河, 水深水淺得走下去試試才知道啊), 文末的鏈接給了很多幫助.
準(zhǔn)備工作
Server端服務(wù)器配置
1.修改系統(tǒng)最大文件打開數(shù)。
臨時(shí)性方案:echo 1200000 > /proc/sys/fs/file-max
永久性方案:在/etc/sysctl.conf
中添加fs.file-max = 1200000
,設(shè)置完成后執(zhí)行命令:sudo sysctl -p,重新裝載配置使其生效歉眷。
查看當(dāng)前系統(tǒng)使用的打開文件描述符數(shù):cat /proc/sys/fs/file-nr
。
~/WorkSpace/Java/vert.x sc cat /proc/sys/fs/file-nr
11069 0 2000000
執(zhí)行命令會(huì)輸出三個(gè)數(shù)值戴陡,第一個(gè)表示當(dāng)前系統(tǒng)已分配使用的文件數(shù)褂萧,第二個(gè)數(shù)為分配后已釋放的(目前已不再使用),第三個(gè)數(shù)是系統(tǒng)最大文件數(shù)嫂冻,等于file-max值胶征。
2.修改進(jìn)程最大文件打開數(shù)。
這個(gè)參數(shù)默認(rèn)值比較小桨仿,我的ubuntu14.04上默認(rèn)是1024個(gè)睛低。如果我們創(chuàng)建大量的鏈接,當(dāng)超過這個(gè)值時(shí)將會(huì)拋出:Too many open files錯(cuò)誤服傍。
首先钱雷,查看本機(jī)的默認(rèn)最大文件打開數(shù):ulimit -n
。如果我們想創(chuàng)建一百萬鏈接的話吹零,這個(gè)值應(yīng)該設(shè)置大于1000000的值罩抗。
臨時(shí)修改方案,這種方案及時(shí)生效灿椅,但是重啟系統(tǒng)后配置丟失套蒂。
ulimit -Sn 1200000
ulimit -Hn 1200000
永久修改方案,需要修改/etc/security/limits.conf茫蛹,*
代表非root用戶操刀。如果只想為某一個(gè)用戶設(shè)置的話*換成用戶名即可。
* hard nofile 1200000
* soft nofile 1200000
root hard nofile 1200000
root soft nofile 1200000
保存修改需重啟系統(tǒng)或者重新登錄使配置生效婴洼。
我們可以啟動(dòng)一個(gè)進(jìn)程來看一下它的最大文件打開數(shù):cat /proc/${pic}/limits
骨坑。以下數(shù)據(jù)是我啟動(dòng)的一個(gè)vertx Server進(jìn)程:
~/WorkSpace/Java/vert.x sc cat /proc/27136/limits
Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 8388608 unlimited bytes
Max core file size 0 unlimited bytes
Max resident set unlimited unlimited bytes
Max processes 46651 46651 processes
Max open files 1200000 1200000 files
Max locked memory 65536 65536 bytes
Max address space unlimited unlimited bytes
Max file locks unlimited unlimited locks
Max pending signals 46651 46651 signals
Max msgqueue size 819200 819200 bytes
Max nice priority 0 0
Max realtime priority 0 0
Max realtime timeout unlimited unlimited us
可以看到open files一行已經(jīng)變成我們的預(yù)設(shè)值。在修改的時(shí)候需要注意一點(diǎn)ulimit的之不能超過系統(tǒng)的max file size柬采。
我的ubuntu 17.04在做這個(gè)修改的時(shí)候遇到一點(diǎn)問題卡啰,設(shè)置完成后重啟系統(tǒng)静稻,啟動(dòng)程序發(fā)現(xiàn)最大打開文件總是4096。折騰了半天發(fā)現(xiàn)是ubuntu系統(tǒng)的bug匈辱。解決方案來自這里振湾。
3.TCP協(xié)議棧優(yōu)化
Client端服務(wù)器配置
1.修改端口范圍。
linux服務(wù)器上端口范圍是0~65535亡脸,其中0~1024是給系統(tǒng)保留的端口范圍押搪。當(dāng)客戶端建立鏈接的時(shí)候會(huì)自動(dòng)的選擇一個(gè)本地可用的端口號(hào),為了能夠最大限度的利用機(jī)器上的端口浅碾,我們需要修改端口范圍大州。
通過命令cat /proc/sys/net/ipv4/ip_local_port_range
我們可以查看當(dāng)前的端口范圍配置。
修改的話我們?cè)?etc/sysctl.conf中添加這行:net.ipv4.ip_local_port_range= 1024 65535
垂谢,然后重載配置厦画。這樣的話一個(gè)client服務(wù)器可以創(chuàng)建六萬多個(gè)鏈接(我本機(jī)測(cè)試64500個(gè)鏈接)。
即便是這樣我們也需要大概17臺(tái)客戶端測(cè)試機(jī)滥朱,這我上哪搞去啊根暑。只能通過虛擬IP來弄了(系統(tǒng)知識(shí)嚴(yán)重匱乏,網(wǎng)上找了很長時(shí)間才搞定)徙邻。接下來我們來配置虛擬IP排嫌。配置虛擬IP我們可以通過ifconfig來搞。比如我們直接執(zhí)行命令:sudo ifconfig eth0:0 192.168.199.100 netmask 255.255.255.0
缰犁。eth0是真實(shí)網(wǎng)卡的名字淳地,eth0:0是虛擬網(wǎng)卡的名字,后邊是網(wǎng)卡綁定的IP帅容。如果要想局域網(wǎng)訪問颇象,這個(gè)IP要和你真實(shí)網(wǎng)卡上的IP在同一網(wǎng)段。本地訪問的話就隨意了并徘。
還有另外一種方式是修改/etc/network/interfaces配置文件夯到。我們添加一下條目:
auto eth0:0
iface eth0 inet static
address 192.168.1.201
netmask 255.255.255.0
broadcast 192.168.1.255
gateway 192.168.1.0
添加完成后,重啟network service:sudo /etc/init.d/networking restart
饮亏。通過執(zhí)行ifconfig便可看到這個(gè)虛擬IP耍贾。
為啥一個(gè)server不能創(chuàng)建超過65536個(gè)鏈接呢?這得需要先了解一下系統(tǒng)是如何標(biāo)識(shí)一個(gè)網(wǎng)絡(luò)套接字(socket)的路幸。當(dāng)系統(tǒng)創(chuàng)建一個(gè)網(wǎng)絡(luò)套接字荐开,會(huì)以{源IP地址,源端口简肴,目的IP地址晃听,目的端口}這樣一個(gè)四元組來唯一表示這個(gè)鏈接。假設(shè)一個(gè)server擁有一個(gè)IP地址,而端口號(hào)最多就是65536個(gè)(極限)因此最大鏈接數(shù)也就固定了能扒。如果我們?cè)谝粋€(gè)server上維持創(chuàng)建100M個(gè)鏈接佣渴,粗略估算一個(gè)IP 60000個(gè)鏈接,我們將需要17個(gè)IP地址初斑。
開始測(cè)試
測(cè)試程序的都是用Vert.x實(shí)現(xiàn)的辛润,源碼在此。代碼是非常簡(jiǎn)單的见秤,沒必要說了砂竖。看看在測(cè)試過程中遇到的一些問題鹃答。
1.當(dāng)連接增加到23萬多的時(shí)候乎澄,服務(wù)器hang住了,不在接受任何鏈接测摔。
...
231000 connections connects in...
232000 connections connects in...
通過dmesg查看系統(tǒng)日志發(fā)了一些線索:
[13662.489289] TCP: too many orphaned sockets
[13662.489299] TCP: too many orphaned sockets
[13662.489304] TCP: too many orphaned sockets
[13662.489311] TCP: too many orphaned sockets
[13662.489317] TCP: too many orphaned sockets
[13662.489323] TCP: too many orphaned sockets
[13662.489330] TCP: too many orphaned sockets
[13662.489335] TCP: too many orphaned sockets
[13662.489341] TCP: too many orphaned sockets
[13662.489348] TCP: too many orphaned sockets
網(wǎng)上搜了一下置济,這是系統(tǒng)耗光了socket內(nèi)存,導(dǎo)致新連接進(jìn)來時(shí)無法分配內(nèi)存锋八。需要調(diào)整一下tcp socket參數(shù)浙于。在tcp_mem三個(gè)值分別代表low,pressure查库,high三個(gè)伐值。
low:當(dāng)TCP使用了低于該值的內(nèi)存頁面數(shù)時(shí)黄琼,TCP不會(huì)考慮釋放內(nèi)存樊销。
pressure:當(dāng)TCP使用了超過該值的內(nèi)存頁面數(shù)量時(shí),TCP試圖穩(wěn)定其內(nèi)存使用脏款,進(jìn)入pressure模式围苫,當(dāng)內(nèi)存消耗低于low值時(shí)則退出pressure狀態(tài)。
high:允許所有tcp sockets用于排隊(duì)緩沖數(shù)據(jù)報(bào)的頁面量撤师,當(dāng)內(nèi)存占用超過此值剂府,系統(tǒng)拒絕分配socket,后臺(tái)日志輸出"TCP: too many of orphaned sockets"剃盾。
打開/etc/sysctl.conf腺占,加入以下配置:
net.ipv4.tcp_mem = 786432 2097152 3145728
net.ipv4.tcp_rmem = 4096 4096 6291456
net.ipv4.tcp_wmem = 4096 4096 6291456
tcp_mem 中的單位是頁,1頁=4096字節(jié)痒谴。所以我們?cè)O(shè)置的最大tcp 內(nèi)存是12G衰伯。tcp_rmem,tcp_wmem單位是byte积蔚,所以最小socket讀寫緩存是4k意鲸。
2.server 又卡住不創(chuàng)建鏈接了。
236000 connections connects in...
237000 connections connects in...
238000 connections connects in...
再次查看系統(tǒng)異常信息。
[ 1465.133062] nf_conntrack: nf_conntrack: table full, dropping packet
[ 1465.133066] nf_conntrack: nf_conntrack: table full, dropping packet
[ 1470.134845] net_ratelimit: 23807 callbacks suppressed
[ 1470.134846] nf_conntrack: nf_conntrack: table full, dropping packet
[ 1470.154131] nf_conntrack: nf_conntrack: table full, dropping packet
[ 1470.154138] nf_conntrack: nf_conntrack: table full, dropping packet
[ 1470.161674] nf_conntrack: nf_conntrack: table full, dropping packet
nf_conntrack就是linux NAT的一個(gè)跟蹤連接條目的模塊怎顾,nf_conntrack模塊會(huì)使用一個(gè)哈希表記錄TCP通訊協(xié)議的創(chuàng)建的鏈接記錄读慎,當(dāng)這個(gè)哈希表滿了的時(shí)候,便會(huì)導(dǎo)致:nf_conntrack: table full, dropping packet
錯(cuò)誤槐雾。那我們接下來修改一下這個(gè)值夭委,還是打開/etc/sysctl.conf文件。
加入以下記錄:
net.netfilter.nf_conntrack_max = 1200000
net.netfilter.nf_conntrack_buckets = 150000
這兩個(gè)值表示conntrack的最大值蚜退,以及哈希的桶數(shù)闰靴。
還有一個(gè)關(guān)于JVM的調(diào)優(yōu),這個(gè)也要注意钻注,剛開始創(chuàng)建幾萬個(gè)的時(shí)候沒什么影響蚂且,但是上20萬之后GC影響逐漸顯著, 停頓次數(shù)和時(shí)間都比較顯著。我這邊server給了兩個(gè)G, client給了5個(gè)G. 調(diào)整之后好很多. 垃圾收集器CMS和Parallel都行.
參考
使用四種框架分別實(shí)現(xiàn)百萬websocket常連接的服務(wù)器
高性能網(wǎng)絡(luò)編程7--tcp連接的內(nèi)存使用
Conntrack Tuning