高并發(fā)原理

同步與異步

同步與異步的重點(diǎn)是在消息通知的方式上俊鱼,也就是調(diào)用結(jié)果通知的方式上蝶棋。同步方式是當(dāng)一個同步調(diào)用發(fā)出后,調(diào)用者要一直等待調(diào)用結(jié)果的通知后叫榕,才能進(jìn)行后續(xù)的執(zhí)行。異步方式是當(dāng)一個異步調(diào)用發(fā)出后姊舵,調(diào)用者不能立即得到調(diào)用結(jié)果的返回晰绎。異步調(diào)用要想獲得結(jié)果一般有兩種方式:主動輪詢異步調(diào)用的結(jié)果、被調(diào)用方通過回調(diào)callback來通知調(diào)用方調(diào)用結(jié)果括丁。

image.png

阻塞與非阻塞

阻塞與非阻塞的重點(diǎn)在于進(jìn)程或線程等待消息時的行為荞下,也就是在等待消息的時候,當(dāng)前進(jìn)程或線程是掛起狀態(tài)還是非掛起狀態(tài)。阻塞方式的阻塞調(diào)用在發(fā)出后尖昏,在消息返回之前仰税,當(dāng)前線程或進(jìn)程是會被掛起的,直到有消息返回抽诉,當(dāng)前進(jìn)程或線程才會被激活陨簇。非阻塞方式的非阻塞消息在發(fā)出后,不會阻塞當(dāng)前進(jìn)程或線程迹淌,而會立即返回河绽。

簡單來說,同步與異步的重點(diǎn)在于消息通知的方式唉窃,阻塞與非阻塞的重點(diǎn)在于等待消息時候的行為耙饰。因此也就有了4種組合:同步阻塞、同步非阻塞纹份、異步阻塞苟跪、異步非阻塞。

阻塞IO與非阻塞IO

一個IO操作實(shí)際上是分成兩個步驟:

  1. 發(fā)起IO請求
    阻塞IO和非阻塞IO的區(qū)別在于第一步矮嫉,發(fā)起IO請求是否會被阻塞削咆,如果阻塞直到完成,那就是傳統(tǒng)的阻塞IO蠢笋,否則就是非阻塞IO拨齐。
  2. 實(shí)際的IO操作
    同步IO與異步IO的區(qū)別在于第二步是否阻塞,如果實(shí)際的IO讀寫阻塞請求進(jìn)程昨寞,那么就是同步IO瞻惋,因此阻塞IO、非阻塞IO援岩、IO復(fù)用歼狼、信號驅(qū)動IO都是同步IO。如果不阻塞享怀,而是操作系統(tǒng)幫助做完IO操作再將結(jié)果返回羽峰,那它就是異步IO。

多進(jìn)程和多線程同步阻塞

最早的服務(wù)器程序都是通過多進(jìn)程或多線程來解決并發(fā)IO的問題添瓷,進(jìn)程模型出現(xiàn)的最早梅屉,從UNIX系統(tǒng)誕生之初就有了進(jìn)程的概念。最早的服務(wù)器端程序一般都是Accept一個客戶端連接就創(chuàng)建一個進(jìn)程鳞贷,然后子進(jìn)程進(jìn)入循環(huán)同步阻塞地與客戶端連接交互坯汤,收發(fā)處理數(shù)據(jù)。

image.png

多線程模式出現(xiàn)要晚一些搀愧,線程與進(jìn)程相比更輕量惰聂,而且線程之間是共享內(nèi)存堆棧的疆偿,所以不同的線程之間交互非常容易實(shí)現(xiàn)。比如搓幌,聊天室程序中客戶端連接之間可以交互杆故,玩家可以任意的向其他人發(fā)消息。用多線程模型實(shí)現(xiàn)非常簡單鼻种,線程中可以直接向某個客戶端連接發(fā)送數(shù)據(jù)反番。如果使用多進(jìn)程模式就需要使用到管道、消息隊(duì)列叉钥、共享內(nèi)存等進(jìn)程間通信IPC的復(fù)雜技術(shù)才能實(shí)現(xiàn)罢缸。

多進(jìn)程和多線程模型的操作流程

<?php
$address = "tcp://0.0.0.0:8000";
$svr = stream_socket_server($address, $errno, $errstr) or die("create server failed");
while(true)
{
  $connect = stream_socket_accept($svr);
  if(pcntl_fork() == 0)
  {
    $request = fread($connect);
    fwrite($response);
    fclose($connect);
    exit(0);
  }
}
  1. 創(chuàng)建一個socket并綁定服務(wù)器端口,然后監(jiān)聽端口投队。
  2. 進(jìn)入while循環(huán)枫疆,阻塞在accept操作上,等待客戶端連接進(jìn)入敷鸦。此時程序會進(jìn)入休眠狀態(tài)息楔,直到有新客戶端發(fā)起connect連接到服務(wù)器,操作系統(tǒng)才會喚醒此進(jìn)程扒披。
  3. 主進(jìn)程在多進(jìn)程模型下通過fork創(chuàng)建子進(jìn)程值依,多線程模型下可以使用pthread_create創(chuàng)建子線程。
  4. 子進(jìn)程創(chuàng)建成功后進(jìn)入while循環(huán)碟案,阻塞在recv調(diào)用上愿险,等待客戶端向服務(wù)器發(fā)送數(shù)據(jù)。當(dāng)服務(wù)器收到數(shù)據(jù)后服務(wù)器程序進(jìn)行處理价说,然后send向客戶端發(fā)送響應(yīng)辆亏。長連接的服務(wù)會持續(xù)與客戶端交互,而短連接服務(wù)一般在收到響應(yīng)后就會close鳖目。
  5. 當(dāng)客戶端連接關(guān)閉時扮叨,子進(jìn)程退出并銷毀所有資源,主進(jìn)程會回收掉此子進(jìn)程领迈。

多進(jìn)程和多進(jìn)程模型的最大問題在于彻磁,進(jìn)程和線程的創(chuàng)建和銷毀開銷很大,因此美喲u辦法應(yīng)用在非常繁忙的服務(wù)器程序上狸捅,對應(yīng)的改進(jìn)版也就是經(jīng)典的Leader-Follower模型衷蜓。

Leader-Follower模型

Leader-Follower模型的特點(diǎn)是程序啟動后會創(chuàng)建n個進(jìn)程,每個子進(jìn)程進(jìn)入Accept薪贫,等待新的連接的進(jìn)入。當(dāng)客戶端連接到服務(wù)器時刻恭,其中一個子進(jìn)程會被喚醒瞧省,開始處理客戶端請求扯夭,并且不再接收新的TCP連接。當(dāng)此連接關(guān)閉時鞍匾,子進(jìn)程會釋放并重新進(jìn)入Accept并參與處理新的連接交洗。

<?php
$address = "tcp://0.0.0.0:8000";
$svr = stream_socket_server($address, $errno, $errmsg) or die("create server failed");
for($i=0; $i<32; $i++)
{
  if(pcntl_fork() == 0)
  {
    while(true)
    {
      $connect = stream_socket_accept($svr);
      if($connect == false)
      {
        continue;
      }
      $request = fread($connect);
      fwrite($response);
      fclose($connect);
    }
    exit(0);
  }
}

Leader-Follower模型的優(yōu)勢在于完全可以復(fù)用進(jìn)程,沒有額外消耗橡淑,性能非常好构拳。很多常見的服務(wù)器程序都是基于此模型的,如Apache梁棠、PHP-FPM置森。

當(dāng)然,多進(jìn)程模型也是存在缺陷的:

  • 多線程模型嚴(yán)重依賴進(jìn)程的數(shù)量解決并發(fā)問題符糊,一個客戶端連接就需要占用一個進(jìn)程凫海,工作進(jìn)程的數(shù)量有多少,并發(fā)處理能力就有多少男娄,但是操作系統(tǒng)可以創(chuàng)建的進(jìn)程數(shù)量是有限的行贪。
  • 多進(jìn)程模型啟動的大量進(jìn)程會帶來額外的進(jìn)程調(diào)度消耗,數(shù)百個進(jìn)程時可能進(jìn)程上下文切換調(diào)度消耗占CPU不到1%可以忽略不計(jì)模闲,如果啟動數(shù)千甚至數(shù)萬個進(jìn)程建瘫,消耗會直線上升。調(diào)度消耗可能占到CPU的100%尸折。
  • 在即時通訊程序中啰脚,單臺服務(wù)器要同時維持上萬、數(shù)十萬翁授、上百萬的鏈接時拣播,多進(jìn)程模型就無法勝任了。
  • 在Web服務(wù)器啟動100個進(jìn)程收擦,如果一個請求消耗100毫秒,100個進(jìn)程可以提供1000QPS塞赂,這樣的處理能力還可以。但是如果請求內(nèi)要調(diào)用外網(wǎng)HTTP接口宴猾,如QQ、微信仇哆、微博登錄時沦辙,耗時會很長,一個請求如果需要10秒油讯,那么一個進(jìn)程1秒就只能處理0.1個請求,100個進(jìn)程只能達(dá)到10QPS陌兑,這樣的處理能力就太差了沈跨。

那么,有沒有一種技術(shù)可以在一個進(jìn)程內(nèi)處理所有并發(fā)IO呢兔综?答案是有饿凛,也就是IO復(fù)用技術(shù)软驰。

IO復(fù)用

IO復(fù)用的歷史和多進(jìn)程一樣長,Linux很早就提供了select系統(tǒng)調(diào)用杀狡,可以在一個進(jìn)程內(nèi)維護(hù)1024個連接贰镣,后來加入poll系統(tǒng)調(diào)用,poll做了一系列改進(jìn)后解決了1024個連接的限制問題碑隆,可以維持任意數(shù)量的連接。但是selectpoll存在一個問題是休玩,它們需要循環(huán)檢測連接是否有事件劫狠。這樣問題就來了,如果服務(wù)器有100w個連接独泞,在某一時間只有一個連接是向服務(wù)器發(fā)送了數(shù)據(jù),select/poll就需要做100w次循環(huán)蜒犯,而其中只會有1次命中荞膘,剩下99w9999次都是無效的,白白浪費(fèi)CPU時間片資源淘菩。

直到Linux2.6內(nèi)核開始提供新的epoll系統(tǒng)調(diào)用屠升,可以維持無限數(shù)量的連接潮改,而且無需輪詢费奸,這才真正解決了C10K問題〗福現(xiàn)在各種高并發(fā)異步IO的服務(wù)器程序都是基于epoll實(shí)現(xiàn)的微服,如Nginx、Node.js以蕴、Erlang、Golnag丛肮。像Node.js這樣單進(jìn)程單線程的程序赡磅,都可以維持超過100wTCP連接宝与,這全部都要?dú)w功于epoll技術(shù)。

IO復(fù)用異步非阻塞

IO復(fù)用異步非阻塞使用經(jīng)典的Reactor反應(yīng)堆模型习劫,它本身不處理任何數(shù)據(jù)收發(fā)诽里,只是監(jiān)視一個socket句柄的事件變化。

Reactor模型可以與多進(jìn)程谤狡、多線程結(jié)合使用,即可以實(shí)現(xiàn)異步非阻塞IO焰宣,又可以利用到多核拒贱。目前流程的異步服務(wù)器程序都是使用這種方式:

  • Nginx:多進(jìn)程Reactor
  • Nginx+Lua:多進(jìn)程Reactor+協(xié)程
  • Golang:單線程Rector+多線程協(xié)程
  • Swoole:多線程Reactor+多進(jìn)程Worker

協(xié)程是什么

協(xié)程從底層技術(shù)角度看實(shí)際上還是異步IO Reactor模型,應(yīng)用層自行實(shí)現(xiàn)了任務(wù)調(diào)度闸天,借助于Reactor切換各個當(dāng)前執(zhí)行的用戶態(tài)線程斜做,但用戶代碼中完全感知不到Reactor的存在。

Apache面對高并發(fā)為什么很無力

Apache處理一個請求是同步阻塞的模式笼吟,每到達(dá)一個請求,Apache都會去fork一個子進(jìn)程去處理這個請求贷帮,直到這個請求處理完畢。面對低并發(fā)民晒,這種模式?jīng)]有什么缺點(diǎn)锄禽。但對于高并發(fā)就是這種模式的缺陷了。

因?yàn)橐粋€客戶端占用一個進(jìn)程磁滚,也就是說進(jìn)程數(shù)量有多少并發(fā)能力就有多少宵晚,但操作系統(tǒng)可以創(chuàng)建的進(jìn)程數(shù)量是有限的。其次搜贤,多進(jìn)程存在進(jìn)程間的切換問題钝凶,進(jìn)程間的切換調(diào)度勢必造成CPU的額外消耗。當(dāng)進(jìn)程數(shù)量達(dá)到成千上萬的時候耕陷,進(jìn)程間的切換就會占用CPU大部分的時間片哟沫,而真正進(jìn)程的執(zhí)行反而占用了CPU的一小部分,這就得不償失了嗜诀。

例如:在即時通訊場景中,單臺服務(wù)器可能要維持?jǐn)?shù)十萬的連接发皿,那么就要啟動數(shù)十萬的進(jìn)程來維持拂蝎,這顯然時不可能的。另外玄货,在調(diào)用外部HTTP接口時,假設(shè)Apache啟動100個進(jìn)程來處理請求松捉,每個請求消耗100毫秒,那么這100個進(jìn)程能提供1000QPS隘世。但是,在調(diào)用HTTP接口時,如QQ登錄蔓钟、微博登錄卵贱,一般耗時較長,假設(shè)一個請求消耗10秒键俱,也就是1個進(jìn)程1秒處理0.1個請求,那么100個進(jìn)程只能達(dá)到10QPS缀辩,這樣的處理能力就未免太差了踪央。

綜上所述,由于Apache采用的是同步阻塞的多進(jìn)程模式健无,在面對高并發(fā)場景時是無能為力的液斜。

Nginx是如何處理高并發(fā)的

傳統(tǒng)的服務(wù)器模型就是這樣,因?yàn)橥阶枞亩噙M(jìn)程模型無法面對高并發(fā)臼膏,那么有沒有一種方式可以在一個進(jìn)程中處理所有的并發(fā)I/O呢示损?當(dāng)然是有的,這也就是I/O復(fù)用技術(shù)夺溢。

所謂的I/O復(fù)用也就是多個I/O可以復(fù)用一個進(jìn)程,由于同步阻塞的方式不適合處理高并發(fā)嘉汰,如果是非阻塞的方式呢状勤?采用非阻塞的模式,當(dāng)一個連接過來的時候密似,由于不阻塞住所以一個進(jìn)程可以同時處理多個連接葫盼。

例如:一個進(jìn)程接收1w個鏈接,這個進(jìn)程每次從頭到尾的詢問每個連接:“你有I/O事件沒贫导?有的話就交給我來處理,沒有的話我一會兒再來問你”闺金。然后進(jìn)程就一直從頭到尾的問這1w個連接峰档。如果這1w個連接都沒有I/O事件,就會造成CPU的空轉(zhuǎn)哎壳,如此效率很低尚卫。

那么能不能引入一個代理,這個代理可以同時觀察許多I/O流事件呢刹泄?當(dāng)沒有I/O事件的時候怎爵,這個進(jìn)程處于阻塞狀態(tài),當(dāng)有I/O事件的時候姆蘸,這個代理就去通知進(jìn)程醒來呢?于是狂秦,早期就出現(xiàn)了兩個代理:selectpoll推捐。

selectpoll代理的原理是:當(dāng)連接有I/O流事件產(chǎn)生的時候,就會去喚醒進(jìn)程去處理堪簿。selectpoll的區(qū)別在于select只能觀察1024個鏈接皮壁,而poll可以觀察無限個連接。

但是進(jìn)程并不知道是哪個連接產(chǎn)生的I/O流事件蛾魄,于是進(jìn)程就挨個去問:“請問畏腕,是你有事情要處理嗎茉稠?”,當(dāng)問了9999遍后發(fā)現(xiàn)原來是第1w個進(jìn)程有事情要處理铭污。那么前面這9999次就白問了膀篮,白白浪費(fèi)了寶貴的CPU時間片。由于selectpoll不知道哪個連接有I/O流事件要處理磅网,所以性能也不是很好筷屡。

如果存在一個代理,每次都能夠知道哪個連接有I/O流事件燎潮,也就可以避免CPU無意義的空轉(zhuǎn)了扼倘。于是,epoll出現(xiàn)了爪喘。epoll代理的原理是:當(dāng)連接有I/O流事件產(chǎn)生的時候,epoll就會去告訴進(jìn)程哪個連接有I/O流事件產(chǎn)生泛啸,然后進(jìn)程就去處理這個進(jìn)程秃症。有了epoll,理論上一個進(jìn)程就可以無限數(shù)量的連接种柑,而且無需輪詢聚请,真正解決了C10K的問題。

Nginx是基于epoll的異步非阻塞的服務(wù)器程序驶赏,可以輕松的處理百萬級并發(fā)連接煤傍。

https://www.cnblogs.com/thrillerz/p/7137682.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市五续,隨后出現(xiàn)的幾起案子龄恋,更是在濱河造成了極大的恐慌,老刑警劉巖它碎,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件链韭,死亡現(xiàn)場離奇詭異煮落,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)旋讹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來睦疫,“玉大人鞭呕,你說我怎么就攤上這事⊥吒猓” “怎么了腋么?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵珊擂,是天一觀的道長。 經(jīng)常有香客問我圣贸,道長扛稽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任锡搜,我火速辦了婚禮瞧掺,結(jié)果婚禮上凡傅,老公的妹妹穿的比我還像新娘。我一直安慰自己哼转,他們只是感情好槽华,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布猫态。 她就那樣靜靜地躺著披摄,像睡著了一般勇凭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上寓盗,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天傀蚌,我揣著相機(jī)與錄音柳譬,去河邊找鬼。 笑死销部,一個胖子當(dāng)著我的面吹牛制跟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播雨膨,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼聊记,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了狰右?” 一聲冷哼從身側(cè)響起舆床,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤挨队,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后盛垦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腾夯,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赌蔑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年娃惯,在試婚紗的時候發(fā)現(xiàn)自己被綠了肥败。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡皿哨,死狀恐怖纽谒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情央勒,我是刑警寧澤澳化,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布缎谷,位于F島的核電站,受9級特大地震影響列林,放射性物質(zhì)發(fā)生泄漏希痴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一过牙、第九天 我趴在偏房一處隱蔽的房頂上張望纺铭。 院中可真熱鬧刀疙,春花似錦、人聲如沸竟纳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽桶略。三九已至,卻和暖如春惶翻,著一層夾襖步出監(jiān)牢的瞬間鹅心,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工颅筋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留输枯,地道東北人用押。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像池充,于是被迫代替她去往敵國和親缎讼。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容