網路的幾種IO模型以及Nginx基本原理

1 網絡IO模型

1.1 網絡IO基本概念理解

IO分別表示輸入(input)和輸出(output)。它描述的是計算機的數據流動的過程,所以IO第一大特徵是有數據的流動;那麼對於IO的整個過程大致上分爲2個部分,第一個部分爲IO的調用,第二個過程爲IO的執行。IO的調用指的就是系統調用,IO的執行指的是在內核中相關數據的處理過程,這個過程是由操做系統完成的,與程序員無關。php

IO多路複用是指內核一旦發現進程指定的一個或者多個IO條件準備讀取,它就通知該進程,目前支持I/O多路複用的系統調用有selectpollepoll,I/O多路複用就是經過一種機制,一個進程能夠監視多個描述符(socket),一旦某個描述符就緒(通常是讀就緒或者寫就緒),可以通知程序進行相應的讀寫操做。java

描述符(socket)在windows中能夠叫作句柄。咱們能夠理解成一個文件對應的ID。IO其實就是對

1.2 對同步 異步 阻塞 非阻塞在網絡中的理解

能夠先看之前寫的文章:對同步 異步 阻塞 非阻塞在網絡中的理解linux

阻塞IO:請求進程一直等待IO準備就緒。
非阻塞IO:請求進程不會等待IO準備就緒。
同步IO操做:致使請求進程阻塞,直到IO操做完成。
異步IO操做:不致使請求進程阻塞。nginx

舉個小例子來理解阻塞,非阻塞,同步和異步的關係,咱們知道編寫一個程序能夠有多個函數,每個函數的執行都是相互獨立的;可是,對於一個程序的執行過程,每個函數都是必須的,那麼若是咱們須要等待一個函數的執行結束而後返回一個結果(好比接口調用),那麼咱們說該函數的調用是阻塞的,對於至少有一個函數調用阻塞的程序,在執行的過程當中,一定存在阻塞的一個過程,那麼咱們就說該程序的執行是同步的,對於異步天然就是全部的函數執行過程都是非阻塞的。

這裏的程序就是一次完整的IO,一個函數爲IO在執行過程當中的一個獨立的小片斷。程序員

咱們知道在Linux操做系統中內存分爲內核空間用戶空間,而全部的IO操做都得得到內核的支持,可是因爲用戶態的進程沒法直接進行內核的IO操做,因此內核空間提供了系統調用,使得處於用戶態的進程能夠間接執行IO操做,IO調用的目的是將進程的內部數據遷移到外部即輸出,或將外部數據遷移到進程內部即輸入。而在這裏討論的數據一般是socket進程內部的數據。web

1.3 五種IO模型

基本原理

image.png
在上圖中,每個客戶端會與服務端創建一次socket鏈接,而服務端獲取鏈接後,對於全部的數據的讀取都得通過操做系統的內核,經過系統調用內核將數據複製到用戶進程的緩衝區,而後才完成客戶端的進程與客戶端的交互。那麼根據系統調用的方式的不一樣分爲阻塞和非阻塞,根據系統處理應用進程的方式不一樣分爲同步和異步。segmentfault

模型1:阻塞式IO

image.png
每一次客戶端產生的socket鏈接其實是一個文件描述符fd,而每個用戶進程讀取的實際上也是一個個文件描述符fd,在該時期的系統調用函數會等待網絡請求的數據的到達和數據從內核空間複製到用戶進程空間,也就是說,不管是第一階段的IO調用仍是第二階段的IO執行都會阻塞,那麼就像圖中所畫的同樣,對於多個客戶端鏈接,只能開闢多個線程來處理。windows

模型2:非阻塞IO模型

image.png
對於阻塞IO模型來講最大的問題就體如今阻塞2字上,那麼爲了解決這個問題,系統的內核所以發生了改變。在內核中socket支持了非阻塞狀態。既然這個socket是不阻塞的了,那麼就可使用一個進程處理客戶端的鏈接,該進程內部寫一個死循環,不斷的詢問每個鏈接的網絡數據是否已經到達。此時輪詢發生在用戶空間,可是該進程依然須要本身處理全部的鏈接,因此該時期爲同步非阻塞IO時期,也即爲NIO。後端

模型3:IO多路複用

在非阻塞IO模型中,雖然解決了IO調用阻塞的問題,可是產生了新的問題,若是如今有1萬個鏈接,那麼用戶線程會調用1萬次的系統調用read來進行處理,在用戶空間這種開銷太大,那麼如今須要解決這個問題,思路就是讓用戶進程減小系統調用,可是用戶本身是實現不了的,因此這就致使了內核發生了進一步變化。在內核空間中幫助用戶進程遍歷全部的文件描述符,將數據準備好的文件描述符返回給用戶進程。該方式是同步阻塞IO,由於在第一階段的IO調用會阻塞進程。數組

IO多路複用是指內核一旦發現進程指定的一個或者多個IO條件準備讀取,它就通知該進程,目前支持I/O多路複用的系統調用有selectpollepoll
,I/O多路複用就是經過一種機制,一個進程能夠監視多個描述符(socket),一旦某個描述符就緒(通常是讀就緒或者寫就緒),可以通知程序進行相應的讀
寫操做。

select/poll

image.png
爲了讓內核幫助用戶進程完成文件描述符的遍歷,內核增長了系統調用select/poll(select與poll本質上沒有什麼不一樣,就是poll減小了文件描述符的個數限制),如今用戶進程只須要調用select系統調用函數,而且將文件描述符所有傳遞給select就可讓內核幫助用戶進程完成全部的查詢,而後將數據準備好的文件描述符再返回給用戶進程,最後用戶進程依次調用其餘系統調用函數完成IO的執行過程。

epoll

在select實現的多路複用中依然存在一些問題。

一、用戶進程須要傳遞全部的文件描述符,而後內核將數據準備好的文件描述符再次傳遞回去,這種數據的拷貝下降了IO的速度。
二、內核依然會執行復雜度爲O(n)的主動遍歷操做。

對於第一個問題,提出了一個共享空間的概念,這個空間爲用戶進程和內核進程所共享,而且提供了mmap系統調用,實現用戶空間和內核空間到共享空間的映射,這樣用戶進程就能夠將1萬個文件描述符寫到共享空間中的紅黑樹上,而後內核將準備就緒的文件描述符寫入共享空間的鏈表中,而用戶進程發現鏈表中有數據了就直接讀取而後調用read執行IO便可。

對於第二個問題,內核引入了事件驅動機制(相似於中斷),再也不主動遍歷全部的文件描述符,而是經過事件驅動的方式主動通知內核該文件描述符的數據準備完畢了,而後內核就將其寫入鏈表中便可。
image.png
對於epoll來講在第一階段的epoll_wait依然是阻塞的,故也是同步阻塞式IO。

模型4:信號驅動式IO

在IO執行的數據準備階段,不會阻塞用戶進程。當用戶進程須要等待數據的時候,會向內核發送一個信號,告訴內核須要數據,而後用戶進程就繼續作別的事情去了,而當內核中的數據準備好以後,內核立馬發給用戶進程一個信號,用戶進程收到信號以後,立馬調用recvfrom,去查收數據。該IO模型使用的較少。
image.png

模型5:異步IO(AIO)

應用進程經過 aio_read 告知內核啓動某個操做,而且在整個操做完成以後再通知應用進程,包括把數據從內核空間拷貝到用戶空間。信號驅動 IO 是內核通知咱們什麼時候能夠啓動一個 IO 操做,而異步 IO 模型是由內核通知咱們 IO 操做什麼時候完成。是真正意義上的無阻塞的IO操做,可是目前只有windows支持AIO,linux內核暫時不支持。
image.png

總結

前四種模型的主要區別於第一階段,由於他們的第二階段都是同樣的:在數據從內核拷貝到應用進程的緩衝區期間,進程都會阻塞。相反,異步 IO 模型在這兩個階段都不會阻塞,從而不一樣於其餘四種模型。
image.png

直接內存與零拷貝

直接內存並非虛擬機運行時數據區的一部分,也不是Java 虛擬機規範中農定義的內存區域。直接內存申請空間耗費更高的性能,直接內存IO讀寫的性能要優於普通的堆內存,對於java程序來講,系統內核讀取堆類的對象須要根據代碼段計算其偏移量來獲取對象地址,效率較慢,不太適合網絡IO的場景,對於直接內存來講更加適合IO操做,內核讀取存放在直接內存中的對象較爲方便,由於其地址就是裸露的進程虛擬地址,不須要jvm翻譯。那麼就可使用mmap開闢一塊直接內存mapbuffer和內核空間共享,而且該直接內存能夠直接映射到磁盤上的文件,這樣就能夠經過調用本地的put而不用調用系統調用write就能夠將數據直接寫入磁盤,RandomAccessFile類就是經過開闢mapbuffer實現的讀寫磁盤。

以消息隊列Kafka來講,有生產者和消費者,對於生產者,從網絡發來一個消息msg而且被拷貝到內核緩衝區,該消息經過Kafka調用recvfrom將內核中的msg讀到隊列中,而後加上消息頭head,再將該消息寫入磁盤。若是沒有mmap的話,就會調用一個write系統調用將該消息寫入內核緩衝區,而後內核將該消息再寫入磁盤。在此過程當中出現一次80中斷和2次拷貝。但實際上Kafka使用的是mmap開闢了直接內存到磁盤的映射,直接使用put將消息寫入磁盤。實際上也是經過內核訪問該共享區域將該消息寫入的磁盤。同時在Kafka中有一個概念叫segment,通常爲1G大小。它會充分利用磁盤的順序性,只追加數據,不修改數據。而mmap會直接開闢1G的直接內存,而且直接與segment造成映射關係,在segment滿了的時候再開闢一個新的segment,清空直接內存而後在與新的segment造成映射關係。
clipboard.png

零拷貝描述的是CPU不執行拷貝數據從一個存儲區域到另外一個存儲區域的任務,這一般用於經過網絡傳輸一個文件時以減小CPU週期和內存帶寬。

在Kafka的消費者讀取數據的時候,若是當前消費者想讀取的數據是否是當前直接內存所映射的segment怎麼辦?若是沒有零拷貝的話,進程會先去調用read讀取,而後數據會從磁盤被拷貝到內核,而後內核再拷貝到Kafka隊列,進程再調用write將數據拷貝到內核緩衝區,最後再發送給消費者。實際上能夠發現,數據沒有必要讀到Kafka隊列,直接讀到內核的緩衝區的時候發送給消費者就好了。實際上,linux內核中有一個系統調用就是實現了這種方式讀取數據——sendfile,它有2個參數,一個是infd(讀取數據的文件描述符),一個是outfd(客戶端的socket文件描述符).消費者只需調用該函數,告訴它須要讀取那個文件就能夠不通過Kafka直接將數據讀到內核,而後由內核寫到消費者進程的緩衝區中。
clipboard.png

2 nginx原理了解

2.1 什麼是nginx

Nginx 是一款自由的、開源的、高性能的HTTP服務器和反向代理服務器;同時也是一個IMAP、POP三、SMTP代理服務器; Nginx 能夠做爲一個HTTP服務器進行網站的發佈處理,另外 Nginx 能夠做爲反向代理進行負載均衡的實現。

2.1.1 nginx的三個主要應用場景

一、靜態資源服務(經過本地文件系統提供服務)
二、緩存、負載均衡服務器
三、API服務(OpenResty)
image.png

2.2 爲何選擇nginx?

  1. 更快
    這表如今兩個方面:一方面,在正常狀況下,單次請求會獲得更快的響應;另外一方面, 在高峯期(若有數以萬計的併發請求), Nginx 能夠比其餘Web服務器更快地響應請求。
  2. 高擴展性
    Nginx 的設計極具擴展性,它徹底是由多個不一樣功能、不一樣層次、不一樣類型且耦合度極低的模塊組成。所以,當對某一個模塊修復 Bug 或進行升級時,能夠專一於模塊自身,無須在乎其餘。
    並且在HTTP模塊中,還設計了 HTTP 過濾器模塊:一個正常的 HTTP 模塊在處理完請求後,會有一串 HTTP 過濾器模塊對請求的結果進行再處理。這樣,當咱們開發一個新的 HTTP 模塊時,不但可使用諸如 HTTP 核心模塊、events模塊、log模塊 等不一樣層次或者不一樣類型的模塊,還能夠原封不動地複用大量已有的 HTTP 過濾器模塊。
    這種低耦合度的優秀設計,造就了 Nginx 龐大的第三方模塊,固然,公開的第三方模塊也如官方發佈的模塊同樣容易使用。
    Nginx 的模塊都是嵌入到二進制文件中執行的,不管官方發佈的模塊仍是第三方模塊都是如此。這使得第三方模塊同樣具有極其優秀的性能,充分利用 Nginx的高併發特性,所以,許多高流量的網站都傾向於開發符合本身業務特性的定製模塊。
  3. 高可靠性
    高可靠性是咱們選擇 Nginx 的最基本條件,由於 Nginx 的可靠性是你們有目共睹的,不少家高流量網站都在覈心服務器上大規模使用 Nginx 。 Nginx 的高可靠性來自於其核心框架代碼的優秀設計、模塊設計的簡單性;另外,官方提供的經常使用模塊都很是穩定,每一個 worker 進程相對獨立,master進程在1個worker進程出錯時能夠快速「拉起」新的 worker 子進程提供服務。
  4. 低內存消耗
    通常狀況下,10000個非活躍的HTTP Keep-Alive鏈接在 Nginx 中僅消耗2.5MB的內存,這是 Nginx 支持高併發鏈接的基礎。
  5. 單機支持10萬以上的併發鏈接
    這是一個很是重要的特性!隨着互聯網的迅猛發展和互聯網用戶數量的成倍增加,各大公司、網站都須要應付海量併發請求,一個可以在峯值期頂住10萬以上併發請求的Server, 無疑會獲得你們的青睞。理論上,Nginx支持的併發鏈接上限取決於內存,固然,可以及時地處理更多的併發請求,是與業務特色緊密相關的。
  6. 熱部署
    master管理進程與 worker 工做進程的分離設計,使得 worker 可以提供熱部署功能,便可以在7×24小時不間斷服務的前提下,升級 worker 的可執行文件。固然,它也支持不中止服務就更新配置項、更換日誌文件等功能。
  7. 最自由的BSD許可協議
    這是 worker 能夠快速發展的強大動力。BSD許可協議不僅是容許用戶無償使用 worker ,它還容許用戶在本身的項目中直接使用或修改 worker 源碼,而後發佈。這吸引了無數開發者繼續爲 worker 貢獻本身的智慧。

2.3 Nginx相關的開源版本

一、阿里巴巴Tengine Tengine是由淘寶網發起的Web服務器項目。它在Nginx的基礎上,針對大訪問量網站的需求,添加了不少高級功能和特性。
二、openresty OpenResty® 是一個基於 Nginx 與 Lua 的高性能 Web 平臺,其內部集成了大量精良的 Lua 庫、第三方模塊以及大多數的依賴項。用於方便地搭建可以處理超高併發、擴展性極高的動態 Web 應用、Web 服務和動態網關。

2.4 Nginx高效的緣由及原理解析

2.4.1 服務器的網絡服務模型

web服務器和客戶端是一對多的關係,Web服務器必須有能力同時爲多個客戶端提供服務。通常來講完成並行處理請求工做有三種方式:

2.4.1.1 單進程阻塞的網絡服務器

image.png
說明:

一、建立一個socket,綁定服務器端口(bind),監聽端口(listen),在PHP中用stream_socket_server一個函數就能完成上面3個步驟
二、進入while循環,阻塞在accept操做上,等待客戶端鏈接進入。此時程序會進入睡眠狀態,直到有新的客戶端發起connect到服務器,操做系統會喚
醒此進程。accept函數返回客戶端鏈接的socket
三、利用fread讀取客戶端socket當中的數據收到數據後服務器程序進行處理而後使用fwrite向客戶端發送響應。長鏈接的服務會持續與客戶端交互,
而短鏈接服務通常收到響應就會close。
缺點: 一次只能處理一個鏈接,不支持多個長鏈接同時處理

2.4.1.2 多進程方式

多進程方式指,服務器每當收到一個客戶端請求時,就有服務器主進程生成一個子進程出來和客戶端創建鏈接進行交互,直到鏈接斷開該子進程就結束了。
image.png
說明:

前面流程一致就不補充了
一、程序啓動後就會建立N個進程。每一個子進程進入 Accept,等待新的鏈接進入。當客戶端鏈接到服務器時,其中一個子進程會被喚醒,開始處理客戶端請求,而且再也不接受新的TCP鏈接。
    當鏈接關閉時,子進程會釋放,從新進入 Accept,參與處理新的鏈接。
    這個模型的優點是徹底能夠複用進程,不須要太多的上下文切換,好比php-fpm基於此模型來處理解析php.
多進程方式的優勢是設計簡單,各個子進程相對獨立,處理客戶端請求時彼此不受干擾;

缺點是操做系統生成一個子進程須要進行內存複製等操做,在資源和時間上會產生必定的開銷;當有大量請求時,會致使系統性能降低;
例如:即時聊天程序,一臺服務器可能要維持數十萬的鏈接,那麼就要啓動數十萬的進程來維持。這顯然不可能

2.4.1.3 多線程方式

多線程方式指每當服務器接收到一個請求後,會由服務器主進程派生出一個線程出來和客戶端進行交互。因爲操做系統產生出一個線程的開銷遠遠小於一個進程的開銷。故多線程方式在很大程度上減輕了Web服務器對系統資源的要求。

缺點:穩定性!假設某個進程忽然關閉會形成整個進程中的全部線程都崩潰。

基於上面的模式咱們發現單個進程每次只能經過每次(accept)處理單個請求,有沒有辦法一次性鏈接多個請求,須要的時候再處理呢?

2.4.1.4 單進程IO複用方式

image.png
說明:

一、保存全部的socket,經過select模型,監聽socket描述符的可讀事件
二、Select會在內核空間監聽一旦發現socket可讀,會從內核空間傳遞至用戶空間,在用戶空間經過邏輯判斷是服務端socket可讀,仍是客戶端
的socket可讀
三、若是是服務端的socket可讀,說明有新的客戶端創建,將socket保留到監聽數組當中
四、若是是客戶端的socket可讀,說明當前已經能夠去讀取客戶端發送過來的內容了,經過fread讀取socket內容,而後fwrite響應給客戶端。
優勢:性能最好!一個進程或線程處理多個請求,不須要額外開銷,性能最好,資源佔用最低。

缺點:穩定性!某個進程或線程出錯,可能致使大量請求沒法處理,甚至致使整個服務宕機,單進程對於大量任務處理乏力。

2.4.1.5 多進程的master-workerIO複用方式

2.4.1.5.1 nginx的基本架構

image.png

1.Nginx啓動後,會產生一個主進程,主進程執行一系列的工做後會產生一個或者多個工做進程
2.在客戶端請求動態站點的過程當中,Nginx服務器還涉及和後端服務器的通訊。Nginx將接收到的Web請求經過代理轉發到後端服務器,由後端服務器進行
數據處理和組織;
3.Nginx爲了提升對請求的響應效率,下降網絡壓力,採用了緩存機制,將歷史應答數據緩存到本地。保障對緩存文件的快速訪問

master進程主要用來管理 worker 進程,具體包括如下主要功能:

(1)接收來自外界的信號。
(2)處理配置文件讀取。
(3)建立,綁定和關閉套接字
(4)啓動,終止和維護配置的工做(worker)進程數
(5)當woker進程退出後(異常狀況下),會自動從新啓動新的woker進程

image.png
worker進程的主要任務是完成具體的任務邏輯。其主要關注點是與客戶端或後端真實服務器(此時 worker做爲中間代理)之間的數據可讀/可寫等I/O交互事件。

(1)接收客戶端請求;
(2)將請求一次送入各個功能模塊進行過濾處理;
(3)與後端服務器通訊,接收後端服務器處理結果;
(4)數據緩存;
(5)響應客戶端請求;

資料來源:
[1] https://segmentfault.com/a/11...[2] 六星教育

相關文章
相關標籤/搜索