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