從 T 跳槽到 A 以後,個人編程語言也從 C++ 轉爲 了 Java。在 T 作的偏服務器端開發,而在 A 更偏向於業務開發。上週在 A 公司組內作了一個《服務器端高性能網絡編程》的分享,我訝異於組內的十我的居然沒有一我的作過直接基於 TCP/IP 協議的開發,更多的是 Web 後臺的業務開發。連 Java 最強大的網絡庫 Netty,用過的人也只有一個。但也不難理解---A 公司的中間件平臺,將業務與底層進行了隔離,讓程序員能夠專心於業務開發。linux
孰優孰劣?不能一律而論,還記得跳槽 A 公司以前面試過 N 公司,面試官問我 HTTP 協議、RPC 框架等知識,我也是隻知其一;不知其二。要什麼 HTTP 協議、要什麼 RPC ,咱們直接 TCP/IP。其實這也是 T 公司基礎設施不夠完善的一種表現,這些在個人另外一篇文章《我在騰訊和阿里的見聞》中談過,感興趣的能夠移步。程序員
做爲 Java Web 程序員,你有想過 Web 服務器(Nginx,Tomcat,Jetty等)是如何接受你的 HTTP 請求的嗎?你知道 其實 HTTP 也是基於 TCP/IP 的文本協議嗎?之因此寫這篇文章,是但願 Java Web 程序員對服務器端的某些工做原理有一些簡單的瞭解,也不至於在面試的時候一問三不知。面試
在編寫服務器端網絡程序時,咱們最多見到阻塞、非阻塞、同步和異步這四個詞。它們的解釋分別以下:編程
經常有人弄不清阻塞/非阻塞與同步/異步之間的關係,容易將他們混爲一談。阻塞/非阻塞更多的用來形容某次調用的屬性(好比 read(),write() 是不是阻塞/非阻塞 )因此應用範圍比較窄;而同步/異步則更上層,一般指各個功能/線程之間的關係(好比 Thread1 和 Thread2 是同步執行仍是異步執行)。windows
服務器端 IO 主要分爲兩種:磁盤 IO 和網絡 IO,在講服務器端高性能網絡編程時更多時候咱們講的是網絡 IO 模型。一次完整的服務器端處理網絡請求流程圖以下(簡化版,以 Web 服務器爲例):服務器
這張圖比較簡單,可是不少人在沒看到這張圖以前確定都覺得每次網絡讀(recvfrom())或者寫(sendto())都是在網卡與用戶進程之間進行操做,其實不是。從上圖能夠看出,數據不管從網卡到用戶空間仍是從用戶空間到網卡都須要通過內核。從磁盤上讀寫數據也是如此。因此就有了 mmap 技術,感興趣的能夠自行百度。應用進程(Web 服務器也屬於應用進程,這裏須要再統一幾個概念:用戶進程、應用程序、Web 服務器程序,它們相對於內核來講都是應用進程,因此後面文章中統一成應用進程)須要經過系統調用(例如recvfrom/sendto)向內核讀寫數據,內核再進一步操做網卡。網絡
根據應用進程系統調用方式的阻塞、非阻塞,操做系統在處理應用程序請求時處理方式的同步、異步處理的不一樣,參考《UNIX 網絡編程卷 I》能夠分爲 5 種 IO 模型:多線程
優勢:編程簡單,適合教學。《UNIX網絡編程卷I》上不少例子都是基於這種模式。 缺點:若是套接字上沒有數據,進程將一直阻塞。這時其餘套接字上有數據也不能進行及時處理。若是是多線程方式,除非鏈接關閉不然線程會一直存在,而線程的建立、維護和銷燬很是消耗資源,因此能創建的鏈接數量很是有限。架構
優勢:代碼編寫相對簡單,進程不會阻塞,能夠在同一線程中處理全部鏈接。併發
缺點:須要頻繁的輪詢,比較耗CPU,在併發量很大的時候將花費大量時間在沒有任何數據的鏈接上輪詢。因此該模型只在專門提供某種功能的系統中才會出現。
優勢:統一管理鏈接,不必定採用多線程的方式,同時也不須要輪詢。只須要阻塞於 select 便可,能夠同時管理多個鏈接。
缺點:當 select/poll/epoll 管理的鏈接數過少時,這種模型將退化成阻塞 IO 模型。而且還多了一次系統調用:一次 select/poll/epoll 一次 recvfrom。
優勢:非阻塞
缺點:在前一個通知信號沒被處理的狀況下,後一個信號來了也不能被處理。因此在信號量大的時候會致使後面的信號不能被及時感知。
注:前 4 種模型都是帶有阻塞部分的,有的阻塞在等待數據準備好,有的阻塞在從內核空間拷貝數據到用戶空間。而這種模型應用進程從調用 aio_read 到數據被拷貝到用戶空間,不用任何阻塞,因此該種模式叫異步 IO 模型。這五種模型的取名和並列方式我是保留意見的,感受容易迷惑讀者。
優勢:沒有任何阻塞,充分利用系統內核將 IO 操做與計算邏輯並行。
缺點:編程複雜、操做系統支持很差。目前只有 windows 下的 iocp 實現了真正的 AIO。linux 下在 2.6 版本中才引入,目前並不完善,因此 Linux 下通常採用多路複用模型。
前四種模型的主要區別於第一階段,由於他們的第二階段都是同樣的:在數據從內核拷貝到應用進程的緩衝區期間,進程阻塞於 recvfrom 調用。相反,異步 IO 模型在這兩個階段都須要處理,從而不一樣於其餘四種模型。
JDK 的網絡編程相關的類、接口雖然不像 C++ 是直接依賴於操做系統的,但它的 IO 模型是離不開以上五種模型的。畢竟這是模型,與語言、操做系統無關。 IO 模型只是高性能網絡編程中的基礎部分,光有好的 IO 模型還不行,咱們還須要好的架構(線程模型)。線程模型是高性能網絡編程的核心部分,在後面的文章中應該還會分析。記得關注公衆號哦,記錄着一個 C++ 程序員轉 Java 的學習之路。