C10K問題

C10K問題

(@tigerfive)[學習筆記][c10k]

前言

今天在看Nginx性能優化的時候,突然看到的C10K,就隨手度娘一波,看到了這個標題<<程序員怎麼會不知道 C10K 問題呢?>>,嚇的我趕緊了解了一波什麼是C10K。對於高性能即時通訊技術(或者說互聯網編程)比較關注的開發者,對C10K問題(即單機1萬個併發連接問題)應該都有所瞭解。“C10K”概念最早由Dan Kegel發佈於其個人站點,即出自其經典的高性能網絡編程經典:《The C10K problem(英文)

C10K問題的由來

  大家都知道互聯網的基礎就是網絡通信,早期的互聯網可以說是一個小羣體的集合。互聯網還不夠普及,用戶也不多,一臺服務器同時在線100個用戶估計在當時已經算是大型應用了,所以並不存在什麼 C10K 的難題。互聯網的爆發期應該是在www網站,瀏覽器,雅虎出現後。最早的互聯網稱之爲Web1.0,互聯網大部分的使用場景是下載一個HTML頁面,用戶在瀏覽器中查看網頁上的信息,這個時期也不存在C10K問題。

  Web2.0時代到來後就不同了,一方面是普及率大大提高了,用戶羣體幾何倍增長。另一方面是互聯網不再是單純的瀏覽萬維網網頁,逐漸開始進行交互,而且應用程序的邏輯也變的更復雜,從簡單的表單提交,到即時通信和在線實時互動,C10K的問題才體現出來了。因爲每一個用戶都必須與服務器保持TCP連接才能進行實時的數據交互,諸如Facebook這樣的網站同一時間的併發TCP連接很可能已經過億。

  這時候問題就來了,最初的服務器都是基於進程/線程模型的,新到來一個TCP連接,就需要分配1個進程(或者線程)。而進程又是操作系統最昂貴的資源,一臺機器無法創建很多進程。如果是C10K就要創建1萬個進程,那麼單機而言操作系統是無法承受的(往往出現效率低下甚至完全癱瘓)。如果是採用分佈式系統,維持1億用戶在線需要10萬臺服務器,成本巨大,也只有Facebook、Google、雅虎等巨頭纔有財力購買如此多的服務器。

  基於上述考慮,如何突破單機性能侷限,是高性能網絡編程所必須要直面的問題。這些侷限和問題最早被Dan Kegel 進行了歸納和總結,並首次成系統地分析和提出解決方案,後來這種普遍的網絡現象和技術侷限都被大家稱爲 C10K 問題。

  簡單來說,C10K 就是 Client 10000 問題,即「在同時連接到服務器的客戶端數量超過 10000 個的環境中,即便硬件性能足夠, 依然無法正常提供服務」,簡而言之,就是單機1萬個併發連接問題。這個概念最早由 Dan Kegel 提出併發佈於其個人站點(http://www.kegel.com/c10k.html)。

技術解讀C10K問題

  爲什麼會這樣呢?因爲計算機的上古時代,比如沒有網絡的 PC 時代,不會有程序員高瞻遠矚的預測到互聯網時代的來臨,也不會想到一臺服務器會創建那麼多的進程,即使在互聯網初期,一臺服務器能有100個在線用戶已經是不得了的事情了。甚至,他們在設計 Unix 的 PID 的時候,採用了有符號的16位整數,這就導致一臺計算機上能夠創建出來的進程無法超過32767個。而計算機自己也得運行一些後臺進程,這樣應用軟件能夠創建的進程數就更少了。

  當然,這個問題隨着技術的發展很快就解決了,現在大部分的個人電腦操作系統可以創建64位的進程,由於數據類型所帶來的進程數上限消失了,但是我們依然不能無限制的創建進程,因爲隨着併發連接數的上升會佔用系統大量的內存,同樣會造成系統的不可用。

  操作系統裏內存管理的主要作用是,進程請求內存的時候爲其分配可用內存,進程釋放後回收內存,並監控內存的使用狀況。爲了提高內存的使用率,現代操作系統需要程序能夠共享內存,並且內存的限制對開發者透明,有些程序佔用了內存空間,但不一定是一直使用的,這樣可以把這部分內存數據序列化到磁盤上,需要的時候再加載到內存裏,這樣內存資源永遠會給最需要的程序使用。於是程序員們發明了虛擬內存(Virtual Memory)。

  虛擬內存技術支持程序訪問比物理內存大得多的內存空間,也使得多個程序共享內存更加高效。物理內存由 RAM 芯片提供,虛擬內存則依靠透明的使用磁盤空間,使程序運行起來好像有了更大的內存空間。

  但是問題依然存在,進程和線程的創建都需要消耗一定的內存,每創建一個棧空間,都會產生內存開銷,當內存使用超過物理內存的時候,一部分數據就會持久化到磁盤上,隨之而來的就是性能的大幅度下降。

C10K問題的本質

  C10K問題本質上是操作系統的問題。對於Web1.0/2.0時代的操作系統而言, 傳統的同步阻塞I/O模型都是一樣的,處理的方式都是requests per second,併發10K和100的區別關鍵在於CPU。

  創建的進程線程多了,數據拷貝頻繁(緩存I/O、內核將數據拷貝到用戶進程空間、阻塞), 進程/線程上下文切換消耗大, 導致操作系統崩潰,這就是C10K問題的本質!

  可見,解決C10K問題的關鍵就是儘可能減少這些CPU等核心計算資源消耗,從而榨乾單臺服務器的性能,突破C10K問題所描述的瓶頸。

C10K解決方案探討

  現在我們早已經突破了 C10K 這個瓶頸,具體的思路就是通過單個進程或線程服務於多個客戶端請求,通過異步編程和事件觸發機制替換輪訓,IO 採用非阻塞的方式,減少不必要的性能損耗,等等。

  底層的相關技術包括 epoll、kqueue、libevent 等,應用層面的解決方案包括 OpenResty、Golang、Node.js 等,比如 OpenResty 的介紹中是這麼說的:

   OpenResty 通過匯聚各種設計精良的 Nginx 模塊,從而將 Nginx 有效地變成一個強大的通用 Web 應用平臺。這樣,Web 開發人員和系統工程師可以使用 Lua 腳本語言調動 Nginx 支持的各種 C 以及 Lua 模塊,快速構造出足以勝任 C10K 乃至 C1000K 以上單機併發連接的高性能 Web 應用系統。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章