1. 程式人生 > >Netty深入分析與Dubbo實戰解析(一)——網路程式設計模型介紹

Netty深入分析與Dubbo實戰解析(一)——網路程式設計模型介紹

Linux網路程式設計模型介紹

Linux核心將所有外部裝置都看作一個檔案來操作,對一個檔案的讀寫操作會呼叫核心提供的系統命令,返回一個file descriptor(fd,檔案描述符)。而對一個socket的讀寫也會有相應的描述符。描述符就是一個數字,它指向核心中的一個結構體(檔案路徑,資料區等一些屬性)。

什麼是同步?什麼是非同步?

同步就是:如果有多個任務或者事件要發生,這些任務或者事件必須逐個地進行,一個事件或者任務的執行會導致整個流程的暫時等待,這些事件沒有辦法併發地執行;

非同步就是:如果有多個任務或者事件發生,這些事件可以併發地執行,一個事件或者任務的執行不會導致整個流程的暫時等待。

什麼是阻塞?什麼是非阻塞?

阻塞就是:當某個事件或者任務在執行過程中,它發出一個請求操作,但是由於該請求操作需要的條件不滿足,那麼就會一直在那等待,直至條件滿足;

非阻塞就是:當某個事件或者任務在執行過程中,它發出一個請求操作,如果該請求操作需要的條件不滿足,會立即返回一個標誌資訊告知條件不滿足,不會一直在那等待。

差異

一些朋友將同步和非同步分別與阻塞和非阻塞畫上等號,事實上,它們是兩組完全不同的概念。注意,理解這兩組概念的區別對於後面IO模型的理解非常重要。

同步和非同步著重點在於多個任務的執行過程中,一個任務的執行是否會導致整個流程的暫時等待;

而阻塞和非阻塞著重點在於發出一個請求操作時,如果進行操作的條件不滿足是否會返會一個標誌資訊告知條件不滿足。

同步和非同步可以看作是多個任務之間的關係,阻塞和非阻塞可以看作一個任務發出請求操作但是條件不滿足時的具體處理方式。

什麼是阻塞IO?什麼是非阻塞IO?

在瞭解阻塞IO和非阻塞IO之前,先看下一個具體的IO操作過程是怎麼進行的。

通常來說,IO操作包括:對硬碟的讀寫、對socket的讀寫以及外設的讀寫。

當用戶執行緒發起一個IO請求操作(本文以讀請求操作為例),核心會去檢視要讀取的資料是否就緒,對於阻塞IO來說,如果資料沒有就緒,則會一直在那等待,直到資料就緒;對於非阻塞IO來說,如果資料沒有就緒,則會返回一個標誌資訊告知使用者執行緒當前要讀的資料沒有就緒。當資料就緒之後,便將資料拷貝到使用者執行緒,這樣才完成了一個完整的IO讀請求操作,也就是說一個完整的IO讀請求操作包括兩個階段:
  1)檢視資料是否就緒;


  2)進行資料拷貝(核心將資料拷貝到使用者執行緒)。
  那麼阻塞(blocking IO)和非阻塞(non-blocking IO)的區別就在於第一個階段,如果資料沒有就緒,在檢視資料是否就緒的過程中是一直等待,還是直接返回一個標誌資訊。

Java中傳統的IO都是阻塞IO,比如通過socket來讀資料,呼叫read()方法之後,如果資料沒有就緒,當前執行緒就會一直阻塞在read方法呼叫那裡,直到有資料才返回;而如果是非阻塞IO的話,當資料沒有就緒,read()方法應該返回一個標誌資訊,告知當前執行緒資料沒有就緒,而不是一直在那裡等待。

什麼是同步IO?什麼是非同步IO?

我們先來看一下同步IO和非同步IO的定義,在《Unix網路程式設計》一書中對同步IO和非同步IO的定義是這樣的:

A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes.
  An asynchronous I/O operation does not cause the requesting process to be blocked.

從字面的意思可以看出:同步IO即 如果一個執行緒請求進行IO操作,在IO操作完成之前,該執行緒會被阻塞;

而非同步IO為 如果一個執行緒請求進行IO操作,IO操作不會導致請求執行緒被阻塞。

事實上,同步IO和非同步IO模型是針對使用者執行緒和核心的互動來說的:

對於同步IO:當用戶發出IO請求操作之後,如果資料沒有就緒,需要通過使用者執行緒或者核心不斷地去輪詢資料是否就緒,當資料就緒時,再將資料從核心拷貝到使用者執行緒;

而非同步IO:只有IO請求操作的發出是由使用者執行緒來進行的,IO操作的兩個階段都是由核心自動完成,然後傳送通知告知使用者執行緒IO操作已經完成。也就是說在非同步IO中,不會對使用者執行緒產生任何阻塞。

這是同步IO和非同步IO關鍵區別所在,同步IO和非同步IO的關鍵區別反映在資料拷貝階段是由使用者執行緒完成還是核心完成。所以說非同步IO必須要有作業系統的底層支援。

注意同步IO和非同步IO與阻塞IO和非阻塞IO是不同的兩組概念。

阻塞IO和非阻塞IO是反映在當用戶請求IO操作時,如果資料沒有就緒,是使用者執行緒一直等待資料就緒,還是會收到一個標誌資訊這一點上面的。也就是說,阻塞IO和非阻塞IO是反映在IO操作的第一個階段,在檢視資料是否就緒時是如何處理的。

unix網路程式設計分類

1. 阻塞I/O模型
2. 非阻塞I/O模型
3. I/O複用模型
4. 訊號驅動I/O模型
5. 非同步I/O

阻塞I/O模型

最常用的I/O模型,所有的檔案操作都是阻塞的。以套接字介面為例:在程序空間中呼叫recvfrom,其系統呼叫直到資料包達到且被複制到應用程序的緩衝區中或者發生錯誤時才返回,在此期間會一直等待,程序在呼叫recvfrom開始到它返回的整段時間內都是阻塞的。
在這裡插入圖片描述

非阻塞I/O模型

recvfrom從應用層到核心的時候,如果該緩衝區沒有資料的話,就直接返回一個EWOULDBOLCK錯誤,一般都對非阻塞I/O模型進行輪詢檢查這個狀態,看核心是不是有資料到來。
在這裡插入圖片描述

I/O複用模型

Linux提供select和poll,程序通過將一個或多個fd傳遞給select或poll系統呼叫,阻塞在select操作上,這樣select/poll可以幫我們偵測多個fd是否處於就緒狀態。select/poll是順序掃描fd是否就緒,而且支援的fd數量有限,因此他們的使用收到一些制約。Linux還提供epoll系統呼叫,epoll使用基於事件驅動方式代替順序掃描,因此效能更高。當fd就緒時,立刻回撥函式rollback。
在這裡插入圖片描述

訊號驅動I/O模型

首先開啟套接字訊號驅動I/O功能,並通過系統呼叫sigaction執行一個訊號處理函式(此係統呼叫會立即返回,程序繼續工作,它是阻塞的)當資料準備就緒時,就為該程序生成一個SIGIO訊號,通過訊號回撥通知應用程式呼叫recvfrom來讀取資料,並通知主迴圈函式處理資料。
在這裡插入圖片描述

非同步I/O

告知核心某個操作,讓核心在整個操作完成後(包括將資料從核心複製到使用者自己的緩衝區)通知我們。這種模型與訊號驅動模型主要的區別是:訊號驅動非同步I/O由核心通知我們何時可以開始一個I/O操作;非同步非同步I/O模型由核心通知我們I/O操作何時已經完成了。
在這裡插入圖片描述