最近處處在爭論這些話題,發現不少人對一些基礎的常識並不瞭解,在此發表一文作一下解釋。此文未必能解答全部問題,各位能有一個大體的瞭解就好。 node
你們都知道互聯網的基礎就是網絡通訊,早期的互聯網能夠說是一個小羣體的集合。互聯網還不夠普及,用戶也很少。一臺服務器同時在線100個用戶估計 在當時已經算是大型應用了。因此並不存在什麼C10K的難題。互聯網的爆發期應該是在www網站,瀏覽器,雅虎出現後。最先的互聯網稱之爲Web1.0, 互聯網大部分的使用場景是下載一個Html頁面,用戶在瀏覽器中查看網頁上的信息。這個時期也不存在C10K問題。 程序員
Web2.0時代到來後就不一樣了,1方面是普及率大大提升了,用戶羣體幾何倍增加。2是互聯網再也不是單純的瀏覽萬維網網頁,逐漸開始進行交互,並且 應用程序的邏輯也變的更復雜,從簡單的表單提交,到即時通訊和在線實時互動。C10K的問題才體現出來了。每個用戶都必須與服務器保持TCP鏈接才能進 行實時的數據交互。Facebook這樣的網站同一時間的併發TCP鏈接可能會過億。 編程
騰訊QQ也是有C10K問題的,只不過他們是用了UDP這種原始的包交換協議來實現的,繞開了這個難題。固然過程確定是痛苦的。若是當時有epoll技術,他們確定會用TCP。後來的手機QQ,微信都採用TCP協議。
這時候問題就來了,最初的服務器都是基於進程/線程模型的,新到來一個TCP鏈接,就須要分配1個進程(或者線程)。而進程又是操做系統最昂貴的資 源,一臺機器沒法建立不少進程。若是是C10K就要建立1萬個進程,那麼操做系統是沒法承受的。若是是採用分佈式系統,維持1億用戶在線須要10萬臺服務 器,成本巨大,也只有Facebook,Google,雅虎纔有財力購買如此多的服務器。這就是C10K問題的本質。 瀏覽器
實際上當時也有異步模式,如:select/poll模型,這些技術都有必定的缺點,如selelct最大不能超過1024,poll沒有限制,但每次收到數據須要遍歷每個鏈接查看哪一個鏈接有數據請求。
既然有了C10K問題,程序員們就開始行動去解決它。因而FreeBSD推出了kqueue,Linux推出了epoll,Windows推出了 IOCP。這些操做系統提供的功能就是爲了解決C10K問題。由於Linux是互聯網企業中使用率最高的操做系統,Epoll就成爲C10K killer、高併發、高性能、異步非阻塞這些技術的代名詞了。 服務器
epoll技術的編程模型就是異步非阻塞回調,也能夠叫作Reactor,事件驅動,事件輪循(EventLoop)。Epoll就是爲了解決 C10K問題而生。使用Epoll技術,使得小公司也能夠玩高併發。不須要購買不少服務器,有幾臺服務器就能夠服務大量用戶。 Nginx,libevent,node.js這些就是Epoll時代的產物。 微信
C10K問題解決後,程序員又提出了更高的挑戰,也就是最近在火熱爭論的C100K,C1M等。Epoll既然能解決C10K,解決什麼 C100K,C1M也是能夠的。只不過這個已經沒有意義了。一個公司有1億用戶難道他買不起1萬臺服務器嘛。WhatsApp有2億用戶,賣了150億美 元。1萬臺服務器最多花費5000萬美圓。 網絡
看到阿里技術保障部的人也在談C10K話題,我要補充一下,搞路由器、交換機、網關、防火牆之類基礎網絡設備的人,就不要參與C10K話題了。咱們說的是應用層程序。
當程序員還沉浸在解決C10K問題帶來的成就感時,一個新的問題被拋出了。異步嵌套回調太TM難寫了。尤爲是Node.js層層回調,縮進了幾十 層,要把程序員逼瘋了。因而一個新的技術被提出來了,那就是協程(coroutine)。這個技術本質上也是異步非阻塞技術,它是將事件回調進行了包裝, 讓程序員看不到裏面的事件循環。程序員就像寫阻塞代碼同樣簡單。好比調用 client->recv() 等待接收數據時,就像阻塞代碼同樣寫。其實是底層庫在執行recv時悄悄保存了一個狀態,好比代碼行數,局部變量的值。而後就跳回到EventLoop 中了。何時真的數據到來時,它再把剛纔保存的代碼行數,局部變量值取出來,又開始繼續執行。 併發
這個就像時間禁止的遊戲同樣,國王對巫師說「我必須立刻獲得寶物,否則就砍了你的腦殼」,巫師唸了一句時間中止的咒語,直到過了1年後勇士們才把寶 物送來。這時候巫師解開咒語,把寶物交給國王。這裏國王就能夠理解成協程,他根本沒感受到時間中止,在他中止到醒來期間發生了什麼他不知道,也不關心。 異步
這就是協程的本質。協程是異步非阻塞的另一種展示形式。Golang,Erlang,Lua協程都是這個模型。 分佈式
再回到同步阻塞這個話題,不知道你們看完協程是否感受獲得,實際上協程和同步阻塞是同樣的。答案是的。因此協程也叫作用戶態進/用戶態線程。區別就在於進程/線程是操做系統充當了EventLoop調度,而協程是本身用Epoll進行調度。
協程的優勢是它比系統線程開銷小,缺點是若是其中一個協程中有密集計算,其餘的協程就不運行了。操做系統進程的缺點是開銷大,優勢是不管代碼怎麼寫,全部進程均可以併發運行。
Erlang解決了協程密集計算的問題,它基於自行開發VM,並不執行機器碼。即便存在密集計算的場景,VM發現某個協程執行時間過長,也能夠進行停止切換。Golang因爲是直接執行機器碼的,因此沒法解決此問題。因此Golang要求用戶必須在密集計算的代碼中,自行Yield。
實際上同步阻塞程序的性能並不差,它的效率很高,不會浪費資源。當進程發生阻塞後,操做系統會將它掛起,不會分配CPU。直到數據到達纔會分配 CPU。多進程只是開多了以後反作用太大,由於進程多了互相切換有開銷。因此若是一個服務器程序只有1000左右的併發鏈接,同步阻塞模式是最好的。
協程雖然是用戶態調度,實際上仍是須要調度的,既然調度就會存在上下文切換。因此協程雖然比操做系統進程性能要好,但總仍是有額外消耗的。而異步回調是沒有切換開銷的,它等同於順序執行代碼。因此異步回調程序的性能是要優於協程模型的。
這裏是指Nginx這種多進程異步非阻塞程序。Node.js/Redis此類程序若是不開多個進程,因爲沒法利用多核計算優點,因此性能並很差。在