隨着互聯網的發展,面對海量用戶高併發業務,傳統的阻塞式的服務端架構模式已經無能爲力。本文(和下篇《高性能網絡編程(六):一文讀懂高性能網絡編程中的線程模型》)旨在爲你們提供有用的高性能網絡編程的I/O模型概覽以及網絡服務進程模型的比較,以揭開設計和實現高性能網絡架構的神祕面紗。php
限於篇幅緣由,請將本文與《高性能網絡編程(六):一文讀懂高性能網絡編程中的線程模型》連起來讀,這樣會讓知識更連貫。html
學習交流:編程
- 即時通信開發交流3羣:185926912[推薦]安全
- 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM》服務器
(本文同步發佈於:http://www.52im.net/thread-1935-1-1.html)網絡
陳彩華(caison):主要從事服務端開發、需求分析、系統設計、優化重構工做,主要開發語言是 Java,現任廣州貝聊服務端研發工程師。多線程
關於廣州貝聊:架構
廣州市貝聊信息科技有限公司成立於2013年8月21日,是一家專一於搭建幼兒園家園共育平臺的信息科技公司。併發
公司產品「貝聊」是中國幼兒園家長工做平臺,致力於經過互聯網產品及定製化解決方案,幫助幼兒園解決展現、通知、溝通等家長工做中的痛點,促進家園關係和諧。貝聊是威創股份(A股幼教第一股)、清華啓迪、網易聯手投資的惟一品牌。負載均衡
截止目前,「貝聊」已覆蓋全國31省份的5萬所幼兒園及機構,註冊用戶超過1000萬,用戶次月留存率高達74%,複合增加率爲18.94%,領跑全行業。
本文是C10K問題系列文章中的第5篇,總目錄以下:
《高性能網絡編程(一):單臺服務器併發TCP鏈接數到底能夠有多少》
《高性能網絡編程(二):上一個10年,著名的C10K併發鏈接問題》
《高性能網絡編程(三):下一個10年,是時候考慮C10M併發問題了》
《高性能網絡編程(四):從C10K到C10M高性能網絡應用的理論探索》
《高性能網絡編程(五):一文讀懂高性能網絡編程中的I/O模型》(本文)
首先看看一個典型互聯網服務端處理網絡請求的典型過程:
由上圖能夠看到,主要處理步驟包括:
1)獲取請求數據,客戶端與服務器創建鏈接發出請求,服務器接受請求(1-3);
2)構建響應,當服務器接收完請求,並在用戶空間處理客戶端的請求,直到構建響應完成(4);
3)返回數據,服務器將已構建好的響應再經過內核空間的網絡 I/O 發還給客戶端(5-7)。
設計服務端併發模型時,主要有以下兩個關鍵點:
1)服務器如何管理鏈接,獲取輸入數據;
2)服務器如何處理請求。
以上兩個關鍵點最終都與操做系統的 I/O 模型以及線程(進程)模型相關,這也是本文和下篇《高性能網絡編程(六):一文讀懂高性能網絡編程中的線程模型》將要介紹的內容。下面先詳細介紹這I/O模型。
介紹操做系統的 I/O 模型以前,先了解一下幾個概念:
1)阻塞調用與非阻塞調用;
2)阻塞調用是指調用結果返回以前,當前線程會被掛起,調用線程只有在獲得結果以後纔會返回;
3)非阻塞調用指在不能馬上獲得結果以前,該調用不會阻塞當前線程。
二者的最大區別在於被調用方在收到請求到返回結果以前的這段時間內,調用方是否一直在等待。
阻塞是指調用方一直在等待並且別的事情什麼都不作;非阻塞是指調用方先去忙別的事情。
同步處理與異步處理:同步處理是指被調用方獲得最終結果以後才返回給調用方;異步處理是指被調用方先返回應答,而後再計算調用結果,計算完最終結果後再通知並返回給調用方。
阻塞、非阻塞和同步、異步的區別(阻塞、非阻塞和同步、異步其實針對的對象是不同的):
1)阻塞、非阻塞的討論對象是調用者;
2)同步、異步的討論對象是被調用者。
recvfrom 函數:
recvfrom 函數(經 Socket 接收數據),這裏把它視爲系統調用。
一個輸入操做一般包括兩個不一樣的階段:
1)等待數據準備好;
2)從內核向進程複製數據。
對於一個套接字上的輸入操做,第一步一般涉及等待數據從網絡中到達。當所等待分組到達時,它被複制到內核中的某個緩衝區。第二步就是把數據從內核緩衝區複製到應用進程緩衝區。
實際應用程序在系統調用完成上面的 2 步操做時,調用方式的阻塞、非阻塞,操做系統在處理應用程序請求時,處理方式的同步、異步處理的不一樣,能夠分爲 5 種 I/O 模型(下面的章節將逐個展開介紹)。(參考《UNIX網絡編程卷1》)
在阻塞式 I/O 模型中,應用程序在從調用 recvfrom 開始到它返回有數據報準備好這段時間是阻塞的,recvfrom 返回成功後,應用進程開始處理數據報。
比喻:一我的在釣魚,當沒魚上鉤時,就坐在岸邊一直等。
優勢:程序簡單,在阻塞等待數據期間進程/線程掛起,基本不會佔用 CPU 資源。
缺點:每一個鏈接須要獨立的進程/線程單獨處理,當併發請求量大時爲了維護程序,內存、線程切換開銷較大,這種模型在實際生產中不多使用。
在非阻塞式 I/O 模型中,應用程序把一個套接口設置爲非阻塞,就是告訴內核,當所請求的 I/O 操做沒法完成時,不要將進程睡眠。
而是返回一個錯誤,應用程序基於 I/O 操做函數將不斷的輪詢數據是否已經準備好,若是沒有準備好,繼續輪詢,直到數據準備好爲止。
比喻:邊釣魚邊玩手機,隔會再看看有沒有魚上鉤,有的話就迅速拉桿。
優勢:不會阻塞在內核的等待數據過程,每次發起的 I/O 請求能夠當即返回,不用阻塞等待,實時性較好。
缺點:輪詢將會不斷地詢問內核,這將佔用大量的 CPU 時間,系統資源利用率較低,因此通常 Web 服務器不使用這種 I/O 模型。
在 I/O 複用模型中,會用到 Select 或 Poll 函數或 Epoll 函數(Linux 2.6 之後的內核開始支持),這兩個函數也會使進程阻塞,可是和阻塞 I/O 有所不一樣。
這兩個函數能夠同時阻塞多個 I/O 操做,並且能夠同時對多個讀操做,多個寫操做的 I/O 函數進行檢測,直到有數據可讀或可寫時,才真正調用 I/O 操做函數。
比喻:放了一堆魚竿,在岸邊一直守着這堆魚竿,沒魚上鉤就玩手機。
優勢:能夠基於一個阻塞對象,同時在多個描述符上等待就緒,而不是使用多個線程(每一個文件描述符一個線程),這樣能夠大大節省系統資源。
缺點:當鏈接數較少時效率相比多線程+阻塞 I/O 模型效率較低,可能延遲更大,由於單個鏈接處理須要 2 次系統調用,佔用時間會有增長。
衆所周之,Nginx這樣的高性能互聯網反向代理服務器大獲成功的關鍵就是得益於Epoll。
在信號驅動式 I/O 模型中,應用程序使用套接口進行信號驅動 I/O,並安裝一個信號處理函數,進程繼續運行並不阻塞。
當數據準備好時,進程會收到一個 SIGIO 信號,能夠在信號處理函數中調用 I/O 操做函數處理數據。
比喻:魚竿上繫了個鈴鐺,當鈴鐺響,就知道魚上鉤,而後能夠專心玩手機。
優勢:線程並無在等待數據時被阻塞,能夠提升資源的利用率。
缺點:信號 I/O 在大量 IO 操做時可能會由於信號隊列溢出致使無法通知。
信號驅動 I/O 儘管對於處理 UDP 套接字來講有用,即這種信號通知意味着到達一個數據報,或者返回一個異步錯誤。
可是,對於 TCP 而言,信號驅動的 I/O 方式近乎無用,由於致使這種通知的條件爲數衆多,每個來進行判別會消耗很大資源,與前幾種方式相比優點盡失。
由 POSIX 規範定義,應用程序告知內核啓動某個操做,並讓內核在整個操做(包括將數據從內核拷貝到應用程序的緩衝區)完成後通知應用程序。
這種模型與信號驅動模型的主要區別在於:信號驅動 I/O 是由內核通知應用程序什麼時候啓動一個 I/O 操做,而異步 I/O 模型是由內核通知應用程序 I/O 操做什麼時候完成。
優勢:異步 I/O 可以充分利用 DMA 特性,讓 I/O 操做與計算重疊。
缺點:要實現真正的異步 I/O,操做系統須要作大量的工做。目前 Windows 下經過 IOCP 實現了真正的異步 I/O。
而在 Linux 系統下,Linux 2.6才引入,目前 AIO 並不完善,所以在 Linux 下實現高併發網絡編程時都是以 IO 複用模型模式爲主。
關於AOI的介紹,請見:《Java新一代網絡編程模型AIO原理及Linux系統AIO介紹》。
從上圖中咱們能夠看出,越日後,阻塞越少,理論上效率也是最優。
這五種 I/O 模型中,前四種屬於同步 I/O,由於其中真正的 I/O 操做(recvfrom)將阻塞進程/線程,只有異步 I/O 模型才與 POSIX 定義的異步 I/O 相匹配。
(本文下篇《高性能網絡編程(六):一文讀懂高性能網絡編程中的線程模型》已發佈,敬請閱讀!)
[1] 網絡編程基礎資料:
《技術往事:改變世界的TCP/IP協議(珍貴多圖、手機慎點)》
《通俗易懂-深刻理解TCP協議(下):RTT、滑動窗口、擁塞處理》
《理論聯繫實際:Wireshark抓包分析TCP 3次握手、4次揮手過程》
《P2P技術詳解(一):NAT詳解——詳細原理、P2P簡介》
《P2P技術詳解(二):P2P中的NAT穿越(打洞)方案詳解》
《P2P技術詳解(三):P2P技術之STUN、TURN、ICE詳解》
《鮮爲人知的網絡編程(一):淺析TCP協議中的疑難雜症(上篇)》
《鮮爲人知的網絡編程(二):淺析TCP協議中的疑難雜症(下篇)》
《鮮爲人知的網絡編程(三):關閉TCP鏈接時爲何會TIME_WAIT、CLOSE_WAIT》
《網絡編程懶人入門(五):快速理解爲何說UDP有時比TCP更有優點》
《網絡編程懶人入門(六):史上最通俗的集線器、交換機、路由器功能原理入門》
《網絡編程懶人入門(八):手把手教你寫基於TCP的Socket長鏈接》
《技術掃盲:新一代基於UDP的低延時網絡傳輸層協議——QUIC詳解》
《現代移動端網絡短鏈接的優化手段總結:請求速度、弱網適應、安全保障》
《移動端IM開發者必讀(一):通俗易懂,理解移動網絡的「弱」和「慢」》
《移動端IM開發者必讀(二):史上最全移動弱網絡優化方法總結》
《從HTTP/0.9到HTTP/2:一文讀懂HTTP協議的歷史演變和設計思路》
《腦殘式網絡編程入門(一):跟着動畫來學TCP三次握手和四次揮手》
《腦殘式網絡編程入門(二):咱們在讀寫Socket時,究竟在讀寫什麼?》
《腦殘式網絡編程入門(三):HTTP協議必知必會的一些知識》
《腦殘式網絡編程入門(四):快速理解HTTP/2的服務器推送(Server Push)》
《以網遊服務端的網絡接入層設計爲例,理解實時通訊的技術挑戰》
>> 更多同類文章 ……
[2] NIO高性能異步網絡編程資料:
《Java新一代網絡編程模型AIO原理及Linux系統AIO介紹》
《開源NIO框架八卦——究竟是先有MINA仍是先有Netty?》
《NIO框架入門(一):服務端基於Netty4的UDP雙向通訊Demo演示》
《NIO框架入門(二):服務端基於MINA2的UDP雙向通訊Demo演示》
《NIO框架入門(三):iOS與MINA二、Netty4的跨平臺UDP雙向通訊實戰》
《NIO框架入門(四):Android與MINA二、Netty4的跨平臺UDP雙向通訊實戰》
《Netty 4.x學習(二):Channel和Pipeline詳解》
《Apache Mina框架高級篇(一):IoFilter詳解》
《Apache Mina框架高級篇(二):IoHandler詳解》
《Apache MINA2.0 開發指南(中文版)[附件下載]》
《實踐總結:Netty3.x升級Netty4.x遇到的那些坑(線程篇)》
《實踐總結:Netty3.x VS Netty4.x的線程模型》
《Twitter:如何使用Netty 4來減小JVM的GC開銷(譯文)》
《Netty乾貨分享:京東京麥的生產級TCP網關技術實踐總結》
>> 更多同類文章 ……
(本文同步發佈於:http://www.52im.net/thread-1935-1-1.html)