【解讀】《Scalable IO in Java》閱讀思考與筆記

《Scalable IO in Java》 出自於java大神Doug Lea,這是一篇關於高性能IO服務的經典文章,文章中梳理了網絡服務架構的演化,提出了Reactor反應器模式,這種設計模式是netty等高性能網絡框架的基礎。

網絡處理流程

服務器對於一次網絡請求的處理大概流程如下
流程

一個線程對應一個網絡連接

在早期的網絡編程中,服務端程序會爲每一個網絡連接開啓一個線程,線程與連接之間一一對應。這種方式簡單清晰也便於理解與實現,但是對於系統資源消耗較大,當客戶端數量增多時,服務器難以承受。
一個線程一個連接

高性能的奧義:分而治之 事件驅動

在上述模型中***每一個線程都要完成讀數據、解碼、業務邏輯處理、編碼、回覆這一系列操作***,對於不同客戶端的連接,除了業務邏輯處理是差異化的之外,其餘四部分都是相同的。那麼按照我們以往的編程習慣是不是應該把公共部分進行抽取整合呢?採用分治的思想,把這一完整的過程分爲一個個細小的任務,然後單獨處理。

上述模型的另外一個缺陷就是阻塞式IO,只有當一個連接完全處理完成之後,這個線程才能處理其他的連接,如果在某個環節有個耗時操作,那麼這個線程會一直處於空閒但又不能工作的狀態,是對於系統資源的浪費。採用事件驅動的方式可以避免因爲阻塞而帶來的等待。

基於事件驅動的架構設計通常比其他架構模型更加有效,因爲可以節省一定的性能資源,事件驅動模式下通常不需要爲每一個客戶端建立一個線程,這意味這更少的線程開銷,更少的上下文切換和更少的鎖互斥,但任務的調度可能會慢一些,而且通常實現的複雜度也會增加,相關功能必須分解成簡單的非阻塞操作,類似與GUI的事件驅動機制,當然也不可能把所有阻塞都消除掉,特別是GC, page faults(內存缺頁中斷)等。由於是基於事件驅動的,所以需要跟蹤服務的相關狀態(因爲你需要知道什麼時候事件會發生);

Reactor模式

Reactor也可以稱作反應器模式,它有以下幾個特點:

  1. 它把網絡處理流程進行拆分,分離了網絡連接的建立與業務邏輯的處理。
  2. Reactor模式中會通過分配適當的handler(處理程序)來響應IO事件,類似與AWT 事件處理線程;
  3. 每個handler執行非阻塞的操作,類似於AWT ActionListeners 事件監聽
  4. 通過將handler綁定到事件進行管理,類似與AWT addActionListener 添加事件監聽;

單線程

單線程
基於此種模式一個線程也可以處理來自多個客戶端的網絡連接,可以利用Java NIO來對此模式進行實現,其處理流程如下:
處理流程
在這種模式下一個線程可同時接受多個客戶端的連接,當某個連接就緒時就可以對其進行處理,因爲是單線程所以對於業務邏輯的處理是串行,所以這種方式帶來的性能提升並沒有那麼高,可以爲每一個業務邏輯新開一個線程進行處理,那就是下面的設計方式了。

單reactor多worker線程模式

多線程
在多處理器場景下,爲實現服務的高性能我們可以有目的的採用多線程模式:
增加Worker線程,專門用於處理非IO操作,因爲通過上面的程序我們可以看到,反應器線程需要迅速觸發處理流程,而如果處理過程也就是process()方法產生阻塞會拖慢反應器線程的性能,所以我們需要把一些非IO操作交給Woker線程來做;

多reactor多worker線程模式

多reactor多worker線程模式
拆分並增加反應器Reactor線程,一方面在壓力較大時可以飽和處理IO操作,提高處理能力;另一方面維持多個Reactor線程也可以做負載均衡使用;線程的數量可以根據程序本身是CPU密集型還是IO密集型操作來進行合理的分配;

用數據說明同樣都是多線程爲啥reactor更好

假設網絡連接的建立需要1秒,業務邏輯處理0.5秒,一個線程所佔資源值爲1%,
現在有50個客戶端連接,用傳統的方式一個線程對應一個連接,需要建立50個線程,佔用50%的系統資源,每個線程用時1.5秒,一共75秒。資源與時間的乘積爲35。

現在有50個客戶端連接,用單reactor多worker線程方式。一個線程處理50個連接,資源與時間的乘積爲1% * 1 * 50=0.5;50個線程進行業務邏輯處理50% * 0.5 * 50=12.5.一共爲13。

核心就在於reactor中只有一個線程進行了阻塞,即把網絡IO這個耗時操縱放在了一個線程裏單獨處理,多線程用於處理業務邏輯,這樣就避免了網絡IO把整個程序拖慢。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章