【原創(chuàng)】高性能網(wǎng)絡(luò)編程(二):上一個(gè)10年,著名的C10K并發(fā)連接問(wèn)題
Posted on 2016-10-21 16:02 Jack Jiang 閱讀(2684) 評(píng)論(0) 編輯 收藏1、前言
對(duì)于高性能即時(shí)通訊技術(shù)(或者說(shuō)互聯(lián)網(wǎng)編程)比較關(guān)注的開(kāi)發(fā)者,對(duì)C10K問(wèn)題(即單機(jī)1萬(wàn)個(gè)并發(fā)連接問(wèn)題)應(yīng)該都有所了解。“C10K”概念最早由Dan Kegel發(fā)布于其個(gè)人站點(diǎn),即出自其經(jīng)典的《The C10K problem (英文PDF版、中文譯文)》一文。
正如你所料,過(guò)去的10年里,高性能網(wǎng)絡(luò)編程技術(shù)領(lǐng)域里經(jīng)過(guò)眾多開(kāi)發(fā)者的努力,已很好地解決了C10K問(wèn)題,大家已開(kāi)始關(guān)注并著手解決下一個(gè)十年要面對(duì)的C10M問(wèn)題(即單機(jī)1千萬(wàn)個(gè)并發(fā)連接問(wèn)題,C10M相關(guān)技術(shù)討論和學(xué)習(xí)將在本系列文章的下篇中開(kāi)始展開(kāi),本文不作深入介紹)。
雖然C10K問(wèn)題已被妥善解決,但對(duì)于即時(shí)通訊應(yīng)用(或其它網(wǎng)絡(luò)編程方面)的開(kāi)發(fā)者而言,研究C10K問(wèn)題仍然價(jià)值巨大,因?yàn)榧夹g(shù)的發(fā)展都是有規(guī)律和線索可循的,了解C10K問(wèn)題及其解決思路,通過(guò)舉一反三,或許可以為你以后面對(duì)類似問(wèn)題提供更多可借鑒的思想和解決問(wèn)題的實(shí)踐思路。而這,也正是撰寫本文的目的所在。
2、學(xué)習(xí)交流
- 即時(shí)通訊開(kāi)發(fā)交流群: 215891622 [推薦]
- 移動(dòng)端IM開(kāi)發(fā)推薦文章:《新手入門一篇就夠:從零開(kāi)發(fā)移動(dòng)端IM》
3、C10K問(wèn)題系列文章
本文是C10K問(wèn)題系列文章中的第2篇,總目錄如下:
- 《高性能網(wǎng)絡(luò)編程(一):?jiǎn)闻_(tái)服務(wù)器并發(fā)TCP連接數(shù)到底可以有多少》
- 《高性能網(wǎng)絡(luò)編程(二):上一個(gè)10年,著名的C10K并發(fā)連接問(wèn)題》(本文)
- 《高性能網(wǎng)絡(luò)編程(三):下一個(gè)10年,是時(shí)候考慮C10M并發(fā)問(wèn)題了》
- 《高性能網(wǎng)絡(luò)編程經(jīng)典:《The C10K problem(英文)》[附件下載]》
4、C10K問(wèn)題的提出者
Dan Kegel:軟件工程師
目前工作在美國(guó)的洛杉磯,當(dāng)前受雇于Google公司。從1978年起開(kāi)始接觸計(jì)算機(jī)編程,是Winetricks的作者、也是Wine 1.0的管理員,同時(shí)也是Crosstool( 一個(gè)讓 gcc/glibc 編譯器更易用的工具套件)的作者。發(fā)表了著名的《The C10K problem》技術(shù)文章,是Java JSR-51規(guī)范的提交者并參與編寫了Java平臺(tái)的NIO和文件鎖,同時(shí)參與了RFC 5128標(biāo)準(zhǔn)中有關(guān)NAT 穿越(P2P打洞)技術(shù)的描述和定義。
5、C10K問(wèn)題的由來(lái)
大家都知道互聯(lián)網(wǎng)的基礎(chǔ)就是網(wǎng)絡(luò)通信,早期的互聯(lián)網(wǎng)可以說(shuō)是一個(gè)小群體的集合。互聯(lián)網(wǎng)還不夠普及,用戶也不多,一臺(tái)服務(wù)器同時(shí)在線100個(gè)用戶估計(jì)在當(dāng)時(shí)已經(jīng)算是大型應(yīng)用了,所以并不存在什么 C10K 的難題。互聯(lián)網(wǎng)的爆發(fā)期應(yīng)該是在www網(wǎng)站,瀏覽器,雅虎出現(xiàn)后。最早的互聯(lián)網(wǎng)稱之為Web1.0,互聯(lián)網(wǎng)大部分的使用場(chǎng)景是下載一個(gè)HTML頁(yè)面,用戶在瀏覽器中查看網(wǎng)頁(yè)上的信息,這個(gè)時(shí)期也不存在C10K問(wèn)題。
Web2.0時(shí)代到來(lái)后就不同了,一方面是普及率大大提高了,用戶群體幾何倍增長(zhǎng)。另一方面是互聯(lián)網(wǎng)不再是單純的瀏覽萬(wàn)維網(wǎng)網(wǎng)頁(yè),逐漸開(kāi)始進(jìn)行交互,而且應(yīng)用程序的邏輯也變的更復(fù)雜,從簡(jiǎn)單的表單提交,到即時(shí)通信和在線實(shí)時(shí)互動(dòng),C10K的問(wèn)題才體現(xiàn)出來(lái)了。因?yàn)槊恳粋€(gè)用戶都必須與服務(wù)器保持TCP連接才能進(jìn)行實(shí)時(shí)的數(shù)據(jù)交互,諸如Facebook這樣的網(wǎng)站同一時(shí)間的并發(fā)TCP連接很可能已經(jīng)過(guò)億。
早期的騰訊QQ也同樣面臨C10K問(wèn)題,只不過(guò)他們是用了UDP這種原始的包交換協(xié)議來(lái)實(shí)現(xiàn)的,繞開(kāi)了這個(gè)難題,當(dāng)然過(guò)程肯定是痛苦的。如果當(dāng)時(shí)有epoll技術(shù),他們肯定會(huì)用TCP。眾所周之,后來(lái)的手機(jī)QQ、微信都采用TCP協(xié)議。
實(shí)際上當(dāng)時(shí)也有異步模式,如:select/poll模型,這些技術(shù)都有一定的缺點(diǎn):如selelct最大不能超過(guò)1024、poll沒(méi)有限制,但每次收到數(shù)據(jù)需要遍歷每一個(gè)連接查看哪個(gè)連接有數(shù)據(jù)請(qǐng)求。
這時(shí)候問(wèn)題就來(lái)了,最初的服務(wù)器都是基于進(jìn)程/線程模型的,新到來(lái)一個(gè)TCP連接,就需要分配1個(gè)進(jìn)程(或者線程)。而進(jìn)程又是操作系統(tǒng)最昂貴的資源,一臺(tái)機(jī)器無(wú)法創(chuàng)建很多進(jìn)程。如果是C10K就要?jiǎng)?chuàng)建1萬(wàn)個(gè)進(jìn)程,那么單機(jī)而言操作系統(tǒng)是無(wú)法承受的(往往出現(xiàn)效率低下甚至完全癱瘓)。如果是采用分布式系統(tǒng),維持1億用戶在線需要10萬(wàn)臺(tái)服務(wù)器,成本巨大,也只有Facebook、Google、雅虎等巨頭才有財(cái)力購(gòu)買如此多的服務(wù)器。
基于上述考慮,如何突破單機(jī)性能局限,是高性能網(wǎng)絡(luò)編程所必須要直面的問(wèn)題。這些局限和問(wèn)題最早被Dan Kegel 進(jìn)行了歸納和總結(jié),并首次成系統(tǒng)地分析和提出解決方案,后來(lái)這種普遍的網(wǎng)絡(luò)現(xiàn)象和技術(shù)局限都被大家稱為 C10K 問(wèn)題。
6、技術(shù)解讀C10K問(wèn)題
C10K 問(wèn)題的最大特點(diǎn)是:設(shè)計(jì)不夠良好的程序,其性能和連接數(shù)及機(jī)器性能的關(guān)系往往是非線性的。
舉個(gè)例子:如果沒(méi)有考慮過(guò) C10K 問(wèn)題,一個(gè)經(jīng)典的基于 select 的程序能在舊服務(wù)器上很好處理 1000 并發(fā)的吞吐量,它在 2 倍性能新服務(wù)器上往往處理不了并發(fā) 2000 的吞吐量。這是因?yàn)樵诓呗圆划?dāng)時(shí),大量操作的消耗和當(dāng)前連接數(shù) n 成線性相關(guān)。會(huì)導(dǎo)致單個(gè)任務(wù)的資源消耗和當(dāng)前連接數(shù)的關(guān)系會(huì)是 O(n)。而服務(wù)程序需要同時(shí)對(duì)數(shù)以萬(wàn)計(jì)的socket 進(jìn)行 I/O 處理,積累下來(lái)的資源消耗會(huì)相當(dāng)可觀,這顯然會(huì)導(dǎo)致系統(tǒng)吞吐量不能和機(jī)器性能匹配。
以上這就是典型的C10K問(wèn)題在技術(shù)層面的表現(xiàn)。這也是為何同樣的功能,大多數(shù)開(kāi)發(fā)人員都能很容易地從功能上實(shí)現(xiàn),但一旦放到大并發(fā)場(chǎng)景下,初級(jí)與高級(jí)開(kāi)發(fā)者對(duì)同一個(gè)功能的技術(shù)實(shí)現(xiàn)所體現(xiàn)出的實(shí)際應(yīng)用效果,則是截然不同的。
所以說(shuō),一些沒(méi)有太多大并發(fā)實(shí)踐經(jīng)驗(yàn)的技術(shù)同行,所實(shí)現(xiàn)的諸如即時(shí)通訊應(yīng)用在內(nèi)的網(wǎng)絡(luò)應(yīng)用,所謂的理論負(fù)載動(dòng)不動(dòng)就宣稱能支持單機(jī)上萬(wàn)、上十萬(wàn)甚至上百萬(wàn)的情況,是經(jīng)不起檢驗(yàn)和考驗(yàn)的。
7、C10K問(wèn)題的本質(zhì)
C10K問(wèn)題本質(zhì)上是操作系統(tǒng)的問(wèn)題。對(duì)于Web1.0/2.0時(shí)代的操作系統(tǒng)而言, 傳統(tǒng)的同步阻塞I/O模型都是一樣的,處理的方式都是requests per second,并發(fā)10K和100的區(qū)別關(guān)鍵在于CPU。
創(chuàng)建的進(jìn)程線程多了,數(shù)據(jù)拷貝頻繁(緩存I/O、內(nèi)核將數(shù)據(jù)拷貝到用戶進(jìn)程空間、阻塞), 進(jìn)程/線程上下文切換消耗大, 導(dǎo)致操作系統(tǒng)崩潰,這就是C10K問(wèn)題的本質(zhì)!
可見(jiàn),解決C10K問(wèn)題的關(guān)鍵就是盡可能減少這些CPU等核心計(jì)算資源消耗,從而榨干單臺(tái)服務(wù)器的性能,突破C10K問(wèn)題所描述的瓶頸。
8、C10K問(wèn)題的解決方案探討
要解決這一問(wèn)題,從純網(wǎng)絡(luò)編程技術(shù)角度看,主要思路有兩個(gè):
- 一個(gè)是對(duì)于每個(gè)連接處理分配一個(gè)獨(dú)立的進(jìn)程/線程;
- 另一個(gè)思路是用同一進(jìn)程/線程來(lái)同時(shí)處理若干連接。
8.1 思路一:每個(gè)進(jìn)程/線程處理一個(gè)連接
這一思路最為直接。但是由于申請(qǐng)進(jìn)程/線程會(huì)占用相當(dāng)可觀的系統(tǒng)資源,同時(shí)對(duì)于多進(jìn)程/線程的管理會(huì)對(duì)系統(tǒng)造成壓力,因此這種方案不具備良好的可擴(kuò)展性。
因此,這一思路在服務(wù)器資源還沒(méi)有富裕到足夠程度的時(shí)候,是不可行的。即便資源足夠富裕,效率也不夠高。總之,此思路技術(shù)實(shí)現(xiàn)會(huì)使得資源占用過(guò)多,可擴(kuò)展性差。
8.2 思路二:每個(gè)進(jìn)程/線程同時(shí)處理多個(gè)連接(IO多路復(fù)用)
IO多路復(fù)用從技術(shù)實(shí)現(xiàn)上又分很多種,我們逐一來(lái)看看下述各種實(shí)現(xiàn)方式的優(yōu)劣。
● 實(shí)現(xiàn)方式1:傳統(tǒng)思路最簡(jiǎn)單的方法是循環(huán)挨個(gè)處理各個(gè)連接,每個(gè)連接對(duì)應(yīng)一個(gè) socket,當(dāng)所有 socket 都有數(shù)據(jù)的時(shí)候,這種方法是可行的。但是當(dāng)應(yīng)用讀取某個(gè) socket 的文件數(shù)據(jù)不 ready 的時(shí)候,整個(gè)應(yīng)用會(huì)阻塞在這里等待該文件句柄,即使別的文件句柄 ready,也無(wú)法往下處理。
實(shí)現(xiàn)小結(jié):直接循環(huán)處理多個(gè)連接。
問(wèn)題歸納:任一文件句柄的不成功會(huì)阻塞住整個(gè)應(yīng)用。
● 實(shí)現(xiàn)方式2:select要解決上面阻塞的問(wèn)題,思路很簡(jiǎn)單,如果我在讀取文件句柄之前,先查下它的狀態(tài),ready 了就進(jìn)行處理,不 ready 就不進(jìn)行處理,這不就解決了這個(gè)問(wèn)題了嘛?于是有了 select 方案。用一個(gè) fd_set 結(jié)構(gòu)體來(lái)告訴內(nèi)核同時(shí)監(jiān)控多個(gè)文件句柄,當(dāng)其中有文件句柄的狀態(tài)發(fā)生指定變化(例如某句柄由不可用變?yōu)榭捎茫┗虺瑫r(shí),則調(diào)用返回。之后應(yīng)用可以使用 FD_ISSET 來(lái)逐個(gè)查看是哪個(gè)文件句柄的狀態(tài)發(fā)生了變化。這樣做,小規(guī)模的連接問(wèn)題不大,但當(dāng)連接數(shù)很多(文件句柄個(gè)數(shù)很多)的時(shí)候,逐個(gè)檢查狀態(tài)就很慢了。因此,select 往往存在管理的句柄上限(FD_SETSIZE)。同時(shí),在使用上,因?yàn)橹挥幸粋€(gè)字段記錄關(guān)注和發(fā)生事件,每次調(diào)用之前要重新初始化 fd_set 結(jié)構(gòu)體。
1 | intselect( int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); |
實(shí)現(xiàn)小結(jié):有連接請(qǐng)求抵達(dá)了再檢查處理。
問(wèn)題歸納:句柄上限+重復(fù)初始化+逐個(gè)排查所有文件句柄狀態(tài)效率不高。
● 實(shí)現(xiàn)方式3:poll 主要解決 select 的前兩個(gè)問(wèn)題:通過(guò)一個(gè) pollfd 數(shù)組向內(nèi)核傳遞需要關(guān)注的事件消除文件句柄上限,同時(shí)使用不同字段分別標(biāo)注關(guān)注事件和發(fā)生事件,來(lái)避免重復(fù)初始化。
實(shí)現(xiàn)小結(jié):設(shè)計(jì)新的數(shù)據(jù)結(jié)構(gòu)提供使用效率。
問(wèn)題歸納:逐個(gè)排查所有文件句柄狀態(tài)效率不高。
● 實(shí)現(xiàn)方式4:epoll既然逐個(gè)排查所有文件句柄狀態(tài)效率不高,很自然的,如果調(diào)用返回的時(shí)候只給應(yīng)用提供發(fā)生了狀態(tài)變化(很可能是數(shù)據(jù) ready)的文件句柄,進(jìn)行排查的效率不就高多了么。epoll 采用了這種設(shè)計(jì),適用于大規(guī)模的應(yīng)用場(chǎng)景。實(shí)驗(yàn)表明,當(dāng)文件句柄數(shù)目超過(guò) 10 之后,epoll 性能將優(yōu)于 select 和 poll;當(dāng)文件句柄數(shù)目達(dá)到 10K 的時(shí)候,epoll 已經(jīng)超過(guò) select 和 poll 兩個(gè)數(shù)量級(jí)。
實(shí)現(xiàn)小結(jié):只返回狀態(tài)變化的文件句柄。
問(wèn)題歸納:依賴特定平臺(tái)(Linux)。
因?yàn)長(zhǎng)inux是互聯(lián)網(wǎng)企業(yè)中使用率最高的操作系統(tǒng),Epoll就成為C10K killer、高并發(fā)、高性能、異步非阻塞這些技術(shù)的代名詞了。FreeBSD推出了kqueue,Linux推出了epoll,Windows推出了IOCP,Solaris推出了/dev/poll。這些操作系統(tǒng)提供的功能就是為了解決C10K問(wèn)題。epoll技術(shù)的編程模型就是異步非阻塞回調(diào),也可以叫做Reactor,事件驅(qū)動(dòng),事件輪循(EventLoop)。Nginx,libevent,node.js這些就是Epoll時(shí)代的產(chǎn)物。
● 實(shí)現(xiàn)方式5:由于epoll, kqueue, IOCP每個(gè)接口都有自己的特點(diǎn),程序移植非常困難,于是需要對(duì)這些接口進(jìn)行封裝,以讓它們易于使用和移植,其中l(wèi)ibevent庫(kù)就是其中之一。跨平臺(tái),封裝底層平臺(tái)的調(diào)用,提供統(tǒng)一的 API,但底層在不同平臺(tái)上自動(dòng)選擇合適的調(diào)用。按照l(shuí)ibevent的官方網(wǎng)站,libevent庫(kù)提供了以下功能:當(dāng)一個(gè)文件描述符的特定事件(如可讀,可寫或出錯(cuò))發(fā)生了,或一個(gè)定時(shí)事件發(fā)生了,libevent就會(huì)自動(dòng)執(zhí)行用戶指定的回調(diào)函數(shù),來(lái)處理事件。目前,libevent已支持以下接口/dev/poll, kqueue, event ports, select, poll 和 epoll。Libevent的內(nèi)部事件機(jī)制完全是基于所使用的接口的。因此libevent非常容易移植,也使它的擴(kuò)展性非常容易。目前,libevent已在以下操作系統(tǒng)中編譯通過(guò):Linux,BSD,Mac OS X,Solaris和Windows。使用libevent庫(kù)進(jìn)行開(kāi)發(fā)非常簡(jiǎn)單,也很容易在各種unix平臺(tái)上移植。一個(gè)簡(jiǎn)單的使用libevent庫(kù)的程序如下:
9、參考資料
[1] 為什么QQ用的是UDP協(xié)議而不是TCP協(xié)議?
[2] 移動(dòng)端IM/推送系統(tǒng)的協(xié)議選型:UDP還是TCP?
[3] 高性能網(wǎng)絡(luò)編程經(jīng)典:《The C10K problem(英文)》[附件下載]
[4] 高性能網(wǎng)絡(luò)編程(一):?jiǎn)闻_(tái)服務(wù)器并發(fā)TCP連接數(shù)到底可以有多少
[5] 《The C10K problem (英文在線閱讀、英文PDF版下載、中文譯文)》
[6] 搜狗實(shí)驗(yàn)室技術(shù)交流文檔《C10K問(wèn)題探討》(52im.net).pdf (350.83 KB)
[7] [通俗易懂]深入理解TCP協(xié)議(上):理論基礎(chǔ)
[8] [通俗易懂]深入理解TCP協(xié)議(下):RTT、滑動(dòng)窗口、擁塞處理
[9] 《TCP/IP詳解 卷1:協(xié)議 (在線閱讀版)》
10、更多資料
《TCP/IP詳解 - 第11章·UDP:用戶數(shù)據(jù)報(bào)協(xié)議》
《TCP/IP詳解 - 第17章·TCP:傳輸控制協(xié)議》
《TCP/IP詳解 - 第18章·TCP連接的建立與終止》
《TCP/IP詳解 - 第21章·TCP的超時(shí)與重傳》
《技術(shù)往事:改變世界的TCP/IP協(xié)議(珍貴多圖、手機(jī)慎點(diǎn))》
《通俗易懂-深入理解TCP協(xié)議(上):理論基礎(chǔ)》
《通俗易懂-深入理解TCP協(xié)議(下):RTT、滑動(dòng)窗口、擁塞處理》
《理論經(jīng)典:TCP協(xié)議的3次握手與4次揮手過(guò)程詳解》
《理論聯(lián)系實(shí)際:Wireshark抓包分析TCP 3次握手、4次揮手過(guò)程》
《計(jì)算機(jī)網(wǎng)絡(luò)通訊協(xié)議關(guān)系圖(中文珍藏版)》
《UDP中一個(gè)包的大小最大能多大?》
《Java新一代網(wǎng)絡(luò)編程模型AIO原理及Linux系統(tǒng)AIO介紹》
《NIO框架入門(一):服務(wù)端基于Netty4的UDP雙向通信Demo演示》
《NIO框架入門(二):服務(wù)端基于MINA2的UDP雙向通信Demo演示》
《NIO框架入門(三):iOS與MINA2、Netty4的跨平臺(tái)UDP雙向通信實(shí)戰(zhàn)》
《NIO框架入門(四):Android與MINA2、Netty4的跨平臺(tái)UDP雙向通信實(shí)戰(zhàn)》
《P2P技術(shù)詳解(一):NAT詳解——詳細(xì)原理、P2P簡(jiǎn)介》
《P2P技術(shù)詳解(二):P2P中的NAT穿越(打洞)方案詳解》
《P2P技術(shù)詳解(三):P2P技術(shù)之STUN、TURN、ICE詳解》
《高性能網(wǎng)絡(luò)編程(一):?jiǎn)闻_(tái)服務(wù)器并發(fā)TCP連接數(shù)到底可以有多少》
《高性能網(wǎng)絡(luò)編程(二):上一個(gè)10年,著名的C10K并發(fā)連接問(wèn)題》
《高性能網(wǎng)絡(luò)編程(三):下一個(gè)10年,是時(shí)候考慮C10M并發(fā)問(wèn)題了》
>> 更多同類文章 ……
(本文同步發(fā)布于:http://www.52im.net/thread-566-1-1.html)
作者:Jack Jiang (點(diǎn)擊作者姓名進(jìn)入Github)
出處:http://www.52im.net/space-uid-1.html
交流:歡迎加入即時(shí)通訊開(kāi)發(fā)交流群 215891622
討論:http://www.52im.net/
Jack Jiang同時(shí)是【原創(chuàng)Java Swing外觀工程BeautyEye】和【輕量級(jí)移動(dòng)端即時(shí)通訊框架MobileIMSDK】的作者,可前往下載交流。
本博文 歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明出處(也可前往 我的52im.net 找到我)。
作者:Jack Jiang (點(diǎn)擊作者姓名進(jìn)入Github)
出處:http://www.52im.net/space-uid-1.html
交流:歡迎加入即時(shí)通訊開(kāi)發(fā)交流群 215891622
討論:http://www.52im.net/
Jack Jiang同時(shí)是【原創(chuàng)Java
Swing外觀工程BeautyEye】和【輕量級(jí)移動(dòng)端即時(shí)通訊框架MobileIMSDK】的作者,可前往下載交流。
本博文
歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明出處(也可前往 我的52im.net 找到我)。