之前有一位讀者向我訴苦,有次面試,好不容易(今年行情大家都懂的)熬到到技術(shù)終面,誰(shuí)知道面試官突然放個(gè)大招問(wèn)他:一臺(tái)服務(wù)器最大能支持多少條 TCP 連接,把他直接給問(wèn)懵逼了。。。。(請(qǐng)自行腦補(bǔ)那尷尬的場(chǎng)面與氣氛)。
所以,今天就來(lái)討論一下這個(gè)問(wèn)題。
一臺(tái)服務(wù)器最大能打開(kāi)的文件數(shù)
限制參數(shù)
我們知道在Linux中一切皆文件,那么一臺(tái)服務(wù)器最大能打開(kāi)多少個(gè)文件呢?Linux上能打開(kāi)的最大文件數(shù)量受三個(gè)參數(shù)影響,分別是:
fs.file-max (系統(tǒng)級(jí)別參數(shù)):該參數(shù)描述了整個(gè)系統(tǒng)可以打開(kāi)的最大文件數(shù)量。但是root用戶不會(huì)受該參數(shù)限制(比如:現(xiàn)在整個(gè)系統(tǒng)打開(kāi)的文件描述符數(shù)量已達(dá)到fs.file-max ,此時(shí)root用戶仍然可以使用ps、kill等命令或打開(kāi)其他文件描述符)。
soft nofile(進(jìn)程級(jí)別參數(shù)):限制單個(gè)進(jìn)程上可以打開(kāi)的最大文件數(shù)。只能在Linux上配置一次,不能針對(duì)不同用戶配置不同的值。
fs.nr_open(進(jìn)程級(jí)別參數(shù)):限制單個(gè)進(jìn)程上可以打開(kāi)的最大文件數(shù)。可以針對(duì)不同用戶配置不同的值。
這三個(gè)參數(shù)之間還有耦合關(guān)系,所以配置值的時(shí)候還需要注意以下三點(diǎn):
如果想加大soft nofile,那么hard nofile參數(shù)值也需要一起調(diào)整。如果因?yàn)閔ard nofile參數(shù)值設(shè)置的低,那么soft nofile參數(shù)的值設(shè)置的再高也沒(méi)有用,實(shí)際生效的值會(huì)按照二者最低的來(lái)。
如果增大了hard nofile,那么fs.nr_open也都需要跟著一起調(diào)整(fs.nr_open參數(shù)值一定要大于hard nofile參數(shù)值)。如果不小心把hard nofile的值設(shè)置的比f(wàn)s.nr_open還大,那么后果比較嚴(yán)重。會(huì)導(dǎo)致該用戶無(wú)法登錄,如果設(shè)置的是*,那么所有用戶都無(wú)法登錄。
如果加大了fs.nr_open,但是是用的echo "xxx" > ../fs/nr_open命令來(lái)修改的fs.nr_open的值,那么剛改完可能不會(huì)有問(wèn)題,但是只要機(jī)器一重啟,那么之前通過(guò)echo命令設(shè)置的fs.nr_open值便會(huì)失效,用戶還是無(wú)法登錄。所以非常不建議使用echo的方式修改內(nèi)核參數(shù)!!!
調(diào)整服務(wù)器能打開(kāi)的最大文件數(shù)示例
假設(shè)想讓進(jìn)程可以打開(kāi)100萬(wàn)個(gè)文件描述符,這里用修改conf文件的方式給出一個(gè)建議。如果日后工作里有類(lèi)似的需求可以作為參考。
vim/etc/sysctl.conf fs.file-max=1100000//系統(tǒng)級(jí)別設(shè)置成110萬(wàn),多留點(diǎn)buffer fs.nr_open=1100000//進(jìn)程級(jí)別也設(shè)置成110萬(wàn),因?yàn)橐WC比hardnofile大
使上面的配置生效sysctl -p
vim/etc/security/limits.conf //用戶進(jìn)程級(jí)別都設(shè)置成100完 softnofile1000000 hardnofile1000000
一臺(tái)服務(wù)器最大能支持多少連接
我們知道TCP連接,從根本上看其實(shí)就是client和server端在內(nèi)存中維護(hù)的一組【socket內(nèi)核對(duì)象】(這里也對(duì)應(yīng)著TCP四元組:源IP、源端口、目標(biāo)IP、目標(biāo)端口),他們只要能夠找到對(duì)方,那么就算是一條連接。那么一臺(tái)服務(wù)器最大能建立多少條連接呢?
由于TCP連接本質(zhì)上可以理解為是client-server端的一對(duì)socket內(nèi)核對(duì)象,那么從理論上將應(yīng)該是【2^32 (ip數(shù)) * 2^16 (端口數(shù))】條連接(約等于兩百多萬(wàn)億)。
但是實(shí)際上由于受其他軟硬件的影響,我們一臺(tái)服務(wù)器不可能能建立這么多連接(主要是受CPU和內(nèi)存限制)。
如果只以ESTABLISH狀態(tài)的連接來(lái)算(這些連接只是建立,但是不收發(fā)數(shù)據(jù)也不處理相關(guān)的業(yè)務(wù)邏輯)那么一臺(tái)服務(wù)器最大能建立多少連接呢?以一臺(tái)4GB內(nèi)存的服務(wù)器為例!
這種情況下,那么能建立的連接數(shù)量主要取決于【內(nèi)存的大小】(因?yàn)槿绻牵〦STABLISH狀態(tài)的空閑連接,不會(huì)消耗CPU(雖然有TCP保活包傳輸,但這個(gè)影響非常小,可以忽略不計(jì))。
我們知道一條ESTABLISH狀態(tài)的連接大約消耗【3.3KB內(nèi)存】,那么通過(guò)計(jì)算得知一臺(tái)4GB內(nèi)存的服務(wù)器,【可以建立100w+的TCP連接】(當(dāng)然這里只是計(jì)算所有的連接都只建立連接但不發(fā)送和處理數(shù)據(jù)的情況,如果真實(shí)場(chǎng)景中有數(shù)據(jù)往來(lái)和處理(數(shù)據(jù)接收和發(fā)送都需要申請(qǐng)內(nèi)存,數(shù)據(jù)處理便需要CPU),那便會(huì)消耗更高的內(nèi)存以及占用更多的CPU,并發(fā)不可能達(dá)到100w+)。
上面討論的都是進(jìn)建立連接的理想情況,在現(xiàn)實(shí)中如果有頻繁的數(shù)據(jù)收發(fā)和處理(比如:壓縮、加密等),那么一臺(tái)服務(wù)器能支撐1000連接都算好的了,所以一臺(tái)服務(wù)器能支撐多少連接還要結(jié)合具體的場(chǎng)景去分析,不能光靠理論值去算。拋開(kāi)業(yè)務(wù)邏輯單純的談并發(fā)沒(méi)有太大的實(shí)際意義。
服務(wù)器的開(kāi)銷(xiāo)大頭往往并不是連接本身,而是每條連接上的數(shù)據(jù)收發(fā),以及請(qǐng)求業(yè)務(wù)邏輯處理!!!
一臺(tái)客戶端機(jī)器最多能發(fā)起多少條連接
我們知道客戶端每和服務(wù)端建立一個(gè)連接便會(huì)消耗掉client端一個(gè)端口。一臺(tái)機(jī)器的端口范圍是【0 ~ 65535】,那么是不是說(shuō)一臺(tái)client機(jī)器最多和一臺(tái)服務(wù)端機(jī)器建立65535個(gè)連接呢(這65535個(gè)端口里還有很多保留端口,可用端口可能只有64000個(gè)左右)?
由TCP連接的四元組特性可知,只要四元組里某一個(gè)元素不同,那么就認(rèn)為這是不同的TCP連接。所以需要分情況討論:
【情況一】、如果一臺(tái)client僅有一個(gè)IP,server端也僅有一個(gè)IP并且僅啟動(dòng)一個(gè)程序,監(jiān)聽(tīng)一個(gè)端口的情況下,client端和這臺(tái)server端最大可建立的連接條數(shù)就是 65535 個(gè)。
因?yàn)樵碔P固定,目標(biāo)IP和端口固定,四元組中唯一可變化的就是【源端口】,【源端口】的可用范圍又是【0 ~ 65535】,所以一臺(tái)client機(jī)器最大能建立65535個(gè)連接。
【情況二】、如果一臺(tái)client有多個(gè)IP(假設(shè)客戶端有 n 個(gè)IP),server端僅有一個(gè)IP并且僅啟動(dòng)一個(gè)程序,監(jiān)聽(tīng)一個(gè)端口的情況下,一臺(tái)client機(jī)器最大能建立的連接條數(shù)是:n * 65535 個(gè)。
因?yàn)槟繕?biāo)IP和端口固定,有 n 個(gè)源IP,四元組中可變化的就是【源端口】+ 【源IP】,【源端口】的可用范圍又是【0 ~ 65535】,所以一個(gè)IP最大能建立65535個(gè)連接,那么n個(gè)IP最大就能建立 n * 65535個(gè)連接了。
以現(xiàn)在的技術(shù),給一個(gè)client分配多個(gè)IP是非常容易的事情,只需要去聯(lián)系你們網(wǎng)管就可以做到。
【情況三】、如果一臺(tái)client僅有一個(gè)IP,server端也僅有一個(gè)IP但是server端啟動(dòng)多個(gè)程序,每個(gè)程序監(jiān)聽(tīng)一個(gè)端口的情況下(比如server端啟動(dòng)了m個(gè)程序,監(jiān)聽(tīng)了m個(gè)不同端口),一臺(tái)client機(jī)器最大能建立的連接數(shù)量為:65535 * m。
源IP固定,目標(biāo)IP固定,目標(biāo)端口數(shù)量為m個(gè),可變化的是源端口,而源端口變化范圍是【0 ~ 65535】,所以一臺(tái)client機(jī)器最大能建立的TCP連接數(shù)量是 65535 * m個(gè)。
其余情況類(lèi)推,但是客戶端的可用端口范圍一般達(dá)不到65535個(gè),受內(nèi)核參數(shù)net.ipv4.ip_local_port_range限制,如果要修改client所能使用的端口范圍,可以修改這個(gè)內(nèi)核參數(shù)的值。
所以,不光是一臺(tái)server端可以接收100w+個(gè)TCP連接,一臺(tái)client照樣能發(fā)出100w+個(gè)連接。
其他
三次握手里socket的全連接隊(duì)列長(zhǎng)度由參數(shù)net.core.somaxconn來(lái)控制,默認(rèn)大小是128,當(dāng)兩臺(tái)機(jī)器離的非常近,但是建立連接的并發(fā)又非常高時(shí),可能會(huì)導(dǎo)致半連接隊(duì)列或全連接隊(duì)列溢出,進(jìn)而導(dǎo)致server端丟棄握手包。然后造成client超時(shí)重傳握手包(至少1s以后才會(huì)重傳),導(dǎo)致三次握手連接建立耗時(shí)過(guò)長(zhǎng)。我們可以調(diào)整參數(shù)net.core.somaxconn來(lái)增加去按連接隊(duì)列的長(zhǎng)度,進(jìn)而減小丟包的影響
有時(shí)候我們通過(guò) ctrl + c方式來(lái)終止了某個(gè)進(jìn)程,但是當(dāng)重啟該進(jìn)程的時(shí)候發(fā)現(xiàn)報(bào)錯(cuò)端口被占用,這種問(wèn)題是因?yàn)椤?a target="_blank">操作系統(tǒng)還沒(méi)有來(lái)得及回收該端口,等一會(huì)兒重啟應(yīng)用就好了】
client程序在和server端建立連接時(shí),如果client沒(méi)有調(diào)用bind方法傳入指定的端口,那么client在和server端建立連接的時(shí)候便會(huì)自己隨機(jī)選擇一個(gè)端口來(lái)建立連接。一旦我們client程序調(diào)用了bind方法傳入了指定的端口,那么client將會(huì)使用我們bind里指定的端口來(lái)和server建立連接。所以不建議client調(diào)用bind方法,bind函數(shù)會(huì)改變內(nèi)核選擇端口的策略
publicstaticvoidmain(String[]args)throwsIOException{ SocketChannelsc=SocketChannel.open(); //客戶端還可以調(diào)用bind方法 sc.bind(newInetSocketAddress("localhost",9999)); sc.connect(newInetSocketAddress("localhost",8080)); System.out.println("waiting.........."); }
在Linux一切皆文件,當(dāng)然也包括之前TCP連接中說(shuō)的socket。進(jìn)程打開(kāi)一個(gè)socket的時(shí)候需要?jiǎng)?chuàng)建好幾個(gè)內(nèi)核對(duì)象,換一句直白的話說(shuō)就是打開(kāi)文件對(duì)象吃內(nèi)存,所以Linux系統(tǒng)基于安全角度考慮(比如:有用戶進(jìn)程惡意的打開(kāi)無(wú)數(shù)的文件描述符,那不得把系統(tǒng)搞奔潰了),在多個(gè)位置都限制了可打開(kāi)的文件描述符的數(shù)量。
內(nèi)核是通過(guò)【hash表】的方式來(lái)管理所有已經(jīng)建立好連接的socket,以便于有請(qǐng)求到達(dá)時(shí)快速的通過(guò)【TCP四元組】查找到內(nèi)核中對(duì)應(yīng)的socket對(duì)象。
在epoll模型中,通過(guò)紅黑樹(shù)來(lái)管理epoll對(duì)象所管理的所有socket,用紅黑樹(shù)結(jié)構(gòu)來(lái)平衡快速刪除、插入、查找socket的效率。
相關(guān)實(shí)際問(wèn)題
在網(wǎng)絡(luò)開(kāi)發(fā)中,很多人對(duì)一個(gè)基礎(chǔ)問(wèn)題始終沒(méi)有徹底搞明白,那就是一臺(tái)機(jī)器最多能支撐多少條TCP連接。不過(guò)由于客戶端和服務(wù)端對(duì)端口使用方式不同,這個(gè)問(wèn)題拆開(kāi)來(lái)理解要容易一些。
注意,這里說(shuō)的是客戶端和服務(wù)端都只是角色,并不是指某一臺(tái)具體的機(jī)器。例如對(duì)于我們自己開(kāi)發(fā)的應(yīng)用程序來(lái)說(shuō),當(dāng)他響應(yīng)客戶端請(qǐng)求的時(shí)候,他就是服務(wù)端。當(dāng)他向MySQL請(qǐng)求數(shù)據(jù)的時(shí)候,他又變成了客戶端。
"too many open files" 報(bào)錯(cuò)是怎么回事,該如何解決
你在線上可能遇到過(guò)too many open files這個(gè)錯(cuò)誤,那么你理解這個(gè)報(bào)錯(cuò)發(fā)生的原理嗎?如果讓你修復(fù)這個(gè)錯(cuò)誤,應(yīng)該如何處理呢?
因?yàn)槊看蜷_(kāi)一個(gè)文件(包括socket),都需要消耗一定的內(nèi)存資源。為了避免個(gè)別進(jìn)程不受控制的打開(kāi)了過(guò)多文件而讓整個(gè)服務(wù)器奔潰,Linux對(duì)打開(kāi)的文件描述符數(shù)量有限制。如果你的進(jìn)程觸發(fā)到內(nèi)核的限制,那么"too many open files" 報(bào)錯(cuò)就產(chǎn)生了。
可以通過(guò)修改fs.file-max 、soft nofile、fs.nr_open這三個(gè)參數(shù)的值來(lái)修改進(jìn)程能打開(kāi)的最大文件描述符數(shù)量。
需要注意這三個(gè)參數(shù)之間的耦合關(guān)系!
一臺(tái)服務(wù)端機(jī)器最大究竟能支持多少條連接
因?yàn)檫@里要考慮的是最大數(shù),因此先不考慮連接上的數(shù)據(jù)收發(fā)和處理,僅考慮ESTABLISH狀態(tài)的空連接。那么一臺(tái)服務(wù)端機(jī)器上最大可以支持多少條TCP連接?這個(gè)連接數(shù)會(huì)受哪些因素的影響?
在不考慮連接上數(shù)據(jù)的收發(fā)和處理的情況下,僅考慮ESTABLISH狀態(tài)下的空連接情況下,一臺(tái)服務(wù)器上最大可支持的TCP連接數(shù)量基本上可以說(shuō)是由內(nèi)存大小來(lái)決定的。
四元組唯一確定一條連接,但服務(wù)端可以接收來(lái)自任意客戶端的請(qǐng)求,所以根據(jù)這個(gè)理論計(jì)算出來(lái)的數(shù)字太大,沒(méi)有實(shí)際意義。另外文件描述符限制其實(shí)也是內(nèi)核為了防止某些應(yīng)用程序不受限制的打開(kāi)【文件句柄】而添加的限制。這個(gè)限制只要修改幾個(gè)內(nèi)核參數(shù)就可以加大。
一個(gè)socket大約消耗3kb左右的內(nèi)存,這樣真正制約服務(wù)端機(jī)器最大并發(fā)數(shù)的就是內(nèi)存,拿一臺(tái)4GB內(nèi)存的服務(wù)器來(lái)說(shuō),可以支持的TCP連接數(shù)量大約是100w+。
一條客戶端機(jī)器最大究竟能支持多少條連接
和服務(wù)端不同的是,客戶端每次建立一條連接都需要消耗一個(gè)端口。在TCP協(xié)議中,端口是一個(gè)2字節(jié)的整數(shù),因此范圍只能是0~65535。那么客戶單最大只能支持65535條連接嗎?有沒(méi)有辦法突破這個(gè)限制,有的話有哪些辦法?
客戶度每次建立一條連接都需要消耗一個(gè)端口。從數(shù)字上來(lái)看,似乎最多只能建立65535條連接。但實(shí)際上我們有兩種辦法破除65535這個(gè)限制。
方式一,為客戶端配置多IP 方式二,分別連接不同的服務(wù)端
所以一臺(tái)client發(fā)起百萬(wàn)條連接是沒(méi)有任何問(wèn)題的。
做一個(gè)長(zhǎng)連接推送產(chǎn)品,支持1億用戶需要多少臺(tái)機(jī)器
假設(shè)你是系統(tǒng)架構(gòu)師,現(xiàn)在老板給你一個(gè)需求,讓你做一個(gè)類(lèi)似友盟upush這樣的產(chǎn)品。要在服務(wù)端機(jī)器上保持一個(gè)和客戶端的長(zhǎng)連接,絕大部分情況下連接都是空閑的,每天也就頂多推送兩三次左右。總用戶規(guī)模預(yù)計(jì)是1億。那么現(xiàn)在請(qǐng)你來(lái)評(píng)估一下需要多少臺(tái)服務(wù)器可以支撐這1億條長(zhǎng)連接。
對(duì)于長(zhǎng)連接推送模塊這種服務(wù)來(lái)說(shuō),給客戶端發(fā)送數(shù)據(jù)只是偶爾的,一般一天也就頂多一兩次。絕大部分情況下TCP連接都是空閑的,CPU開(kāi)銷(xiāo)可以忽略。
再基于內(nèi)存來(lái)考慮,假設(shè)服務(wù)器內(nèi)存是128G的,那么一臺(tái)服務(wù)器可以考慮支持500w條并發(fā)。這樣會(huì)消耗掉大約不到20GB內(nèi)存用來(lái)保存這500w條連接對(duì)應(yīng)的socket。還剩下100GB以上的內(nèi)存來(lái)應(yīng)對(duì)接收、發(fā)送緩沖區(qū)等其他的開(kāi)銷(xiāo)足夠了。所以,一億用戶,僅僅需要20臺(tái)服務(wù)器就差不多夠用了!
編輯:黃飛
-
服務(wù)器
+關(guān)注
關(guān)注
12文章
9124瀏覽量
85332 -
Server
+關(guān)注
關(guān)注
0文章
90瀏覽量
24029 -
TCP
+關(guān)注
關(guān)注
8文章
1353瀏覽量
79056 -
端口
+關(guān)注
關(guān)注
4文章
964瀏覽量
32052
原文標(biāo)題:面試官:一臺(tái)服務(wù)器最大能支持多少條 TCP 連接?問(wèn)倒一大片。。。
文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論