Netty — 線程模型

一.前言

衆所周知,netty是高性能的緣由源於其使用的是NIO,可是這只是其中一方面緣由,其IO模型上決定的。另外一方面源於其線程模型的設計,良好的線程模型設計,可以減小線程上下文切換,減小甚至避免鎖的競爭(無鎖化設計)帶來的開銷。java

本篇文章將介紹netty的線程模型設計,主要從如下幾個方面:react

  • Reactor模式
  • Scalable IO in Java
  • Netty中的線程模型


二.Reactor模式

Reactor模式是一種軟件程序設計模式,它由Jim Coplien和Douglas C. Schmidt在1995年發佈,主要用於處理一個或者多個客戶端發起請求。設計模式

從一個客戶端鏈接到日誌服務器,而後發送請求的兩個流程來看Reactor模式。其中有三種組件:服務器

  • Dispacther:分發客戶端的請求事件
  • Acceptor:接受客戶端鏈接事件
  • Handler:處理客戶端發起的請求事件

1.客戶端鏈接到日誌服務器

  1. 日誌服務端註冊Acceptor至Dispatcher
  2. 日誌服務端調用分發器內部的handle_events方法
  3. 分發器開始在多路監聽器(通常爲OS的select、epoll)上等待客戶端請求
  4. 客戶端鏈接至日誌服務器
  5. 分發器通知Acceptor鏈接事件
  6. Accetor接受一個新的鏈接

2.客戶端發送記錄日誌的請求

  1. 客戶端使用上部分創建的鏈接發送記錄日誌的請求
  2. 客戶端分發器將記錄日誌的時間通知給Handler
  3. Handler處理讀請求而後內部在傳遞給下個Handler繼續處理
  4. 最終Handler進行寫響應
  5. 返回到分發器,分發器繼續事件循環等待處理下個事件

以上的幾種組件的做用和處理鏈接和請求事件的模式就是Reactor模式。網絡


三.Scalable IO in Java

以上的Reactor模式只是簡單的設計模型,對於每種程序語言設計而言,仍然須要作一些改變。基於Java的NIO如何使用該模式構建高性能可伸縮的服務,併發大神Doug Lea在他的網站上發佈過一篇論文《Scalable IO in Java》。多線程

這篇論文中主要談及的話題是如何構建高性能可擴展的IO,其中就是基於Reactor模式進行了演進。併發

其中涉及到如下組件:ide

  • Reactor: 響應IO事件,分發至相應的Handlers
  • Handlers: 執行非阻塞的IO操做,

1.單線程Reactor模式

  1. 其中客戶端發送請求至服務端,Reactor響應其IO事件。
  2. 若是是創建鏈接的請求,則將其分發至acceptor,由其接受鏈接,而後再將其註冊至分發器。
  3. 若是是讀請求,則分發至Handler,由其讀出請求內容,而後對內容解碼,而後處理計算,再對響應編碼,最後發送響應

在整個過程當中都是使用單線程,不管是Reactor線程和後續的Handler處理都只使用一個線程。oop

可是單線程無疑會下降性能,因此須要增長線程提供擴展。性能

2. 多線程Reactor模式

爲了可以提升擴展性,須要在單線程的模型上增長線程,主要從兩個方面利用多線程發揮多核的應用優點:

  1. Worker Threads,Reactor應該可以快速的觸發事件,防止Handler處理的延遲Reactor的響應致使事件積累,最終致使客戶端鏈接請求的積壓,甚至服務端的句柄數耗盡,服務中止響應。因此在Handlers的處理中使用工做多線程
  2. Multiple Reactor Threads,使用多個Reactor線程用於響應客戶端發起的事件,可使用多個Reactor分擔負載

以上多線程的Reactor處理模式中,Reactor線程仍然是單線程,負責acceptor和IO read/send。可是對於請求的解碼以及業務處理和響應的編碼都是有work thread pool負責。

3.多Reactor模式

上述的多線程模式解決了Handler下降Reactor的響應,同時也提高了Handler的處理效率。可是Reactor仍然是單線程,對於大量的網絡事件,其仍然有負載壓力。爲了可以使用多線程分擔壓力,演進出多Reactor:

其中主Reactor響應用戶的鏈接事件,而後分發給acceptor,由其建立新的子Reactor。多個子Reactor分別處理各自的IO事件,好比read/write,而後再將其交給work thread pool進行解碼,業務處理,編碼。

多Reactor的設計經過將TCP鏈接創建和IO read/write事件分離至不一樣的Reactor,從而分擔單個Reactor的壓力,提高其響應能力。


四.Netty中的線程模型

在認識了Reactor設計模式和基於Reactor構建高性能可擴展的IO後,再來看netty的線程模型就顯得簡單的多了。

netty的線程模型設計正是Reactor模式的變種。以上的三種Reactor模式,在netty中都能很是好的獲得了支持。在netty中主要經過參數配置來切換以上的各類模式。

netty中有EventLoopGroup和EventLoop兩個類,它們是實現Reactor的關鍵之所在。EventLoop正如其名,其中包包含一個Selector選擇器和一段循環邏輯。經過不斷循環獲取Selector上的就緒事件而後進行處理。EventLoopGroup是包含一組EventLoop的組,經過其能夠產生一個EventLoop。

在閱讀了netty官網給出的Demo後,能夠知道,在建立一個Server時都會建立兩個EventLoopGroup,分別爲boss和work。前者用戶Main Reactor,後者用於Sub Reactor和WorkThreadPool。

每次Main Reactor經過Selector獲得客戶端創建鏈接的請求後,就從work EventLoopGroup中獲取一個EventLoop,而後將創建的鏈接對應的Socket抽象SocketChannel綁定到EventLoop上,造成了新的Sub Reactor。

在瞭解了netty的線程模型後,下面首先看下各類模式下的netty的參數配置。

1.單線程Reactor配置

經過構造一個EventLoop,將其用做Reactor和WorkThread,便是單線程模式。

EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
EventLoop bossLoop = eventLoopGroup.next();
EventLoop workLoop = reactorLoop;
ServerBootstrap b = new ServerBootstrap();
b.group(reactorLoop, workLoop);

boss和work使用同一個EventLoop,能夠實現單線程Reactor。

2.多線程Reactor配置

Reactor使用單線程,而後Work使用多線程,便是多線程模型。

EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
EventLoop bossLoop = eventLoopGroup.next();
EventLoopGroup workLoopGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(reactorLoop, workLoopGroup);

3.多Reactor模式

以上的多線程Reactor模式,即是多Reactor模式。bossLoop是主Reactor,其經過事件循環建立TCP鏈接,而後將鏈接的SocketChannel抽象綁定到workLoopGroup中的EventLoop上,造成Sub Reactor。

只是Main Reactor是單線程進行事件循環。雖然也能夠構造多線程,可是沒有什麼實際意義。由於netty中在綁定端口時只會使用Group中的一個EventLoop綁定到Selector上,便是使用了EventLoopGroup。

固然對於同個應用若是監聽多個端口,使用多個ServerBootStrap共享一個boss,那樣Main Reactor也是多線程模式,纔有意義。

參考

Scalable IO in Java
Reactor Pattern

相關文章
相關標籤/搜索