引言
我們一直都說Netty
是高性能服務器,那麼它到底爲什麼是高性能應用呢?線程模型直接影響着網絡應用的性能狀況,本文將從Netty
的多線程模型出發揭開其高性能特性的神祕面紗。
多線程模型
(1)傳統IO模型的問題
·如果我們自己是Netty
網絡應用的設計者,想要設計出高性能的網絡應用,首先要面對的問題就是如何解決網絡編程的性能瓶頸。那麼網絡應用的性能瓶頸是什麼呢?我們都知道傳統的網絡應用使用的是BIO
模型,也就是阻塞式IO
。網絡程序處理當中的read()
以及write()
操作都會阻塞當前線程的。所以在傳統的IO
模型當中每一個socket
連接都會有單獨的線程來進行處理。
但是在當前的互聯網時代,客戶端的連接可能是百萬、千萬甚至上億級別的,服務器不可能創建這麼多的線程來處理客戶端請求。這就是傳統IO
網絡連接的性能瓶頸所在。
(2)模型優化
如上一節內容所述,傳統的IO
模型的性能瓶頸在於服務端需要爲每一個socket
連接分配線程來滿足業務處理的需要。那麼有沒有一種方法可以不需要建立那麼多連接也可以處理客戶端的請求呢?如果要用一個線程處理多個請求,那麼BIO
是無法實現的。所以我們可採用java
中的NIO
來完成此部分的優化操作。Netty
的做法是採用Reactor
模式,所謂Reactor
模式就是是一個使用了同步非阻塞的I/O多路複用機制的模式。這裏不再展開說明。
在Netty
的世界中,EventLoopGroup
是一個非常重要的核心概念。所謂EventLoopGroup
就是就是EventLoop
的集合,即爲事件循環。其中NioEventLoop
負責輪詢多路複用器,獲取已經處於就緒狀態的通道,執行網絡的連接、客戶端請求接入、讀和寫相關操作。bossGroup
就用來處理連接請求的,而 workerGroup
是用來處理讀寫請求的。bossGroup
處理完連接請求後,會將這個連接提交給 workerGroup
來處理, workerGroup
裏面有多個 EventLoop
, workerGroup
就是用來處理實際讀寫操作的。那新的連接會交給哪個 EventLoop
來處理呢?這就需要一個負載均衡算法,Netty
中使用的就是輪詢算法。Netty
支持多種Reactor
模式,如單線程模型、多線程模型以及主從多線程模型,用戶可以根據實際場景進行啓動參數中進行設置來切換對應的模式。
(3)代碼分析
a、單線程模型
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup)
.channel(NioServerSocketChannel.class)
...
}
...
如上代碼可知,創建了一個參數爲1的bossGroup
,該EventLoopGroup 主要用於接收客戶端的連接,即爲單線程模型。
b、多線程模型
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
...
}
...
與單線程模式不同,多線程模式創建了workerGroup
來處理IO
操作,數量默認爲CPU數量的兩倍。
c、主從多線程模型
在該模型中,用於接收客戶端連接的線程不再是單一的NIO
線程了,而是一個線程池。
//創建boss線程組,處理客戶端連接
EventLoopGroup bossGroup = new NioEventLoopGroup(2);
// 創建worker線程組用於SocketChannel 的數據讀寫
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// 創建 ServerBootstrap 對象
ServerBootstrap b = new ServerBootstrap();
// 設置EventLoopGroup
b.group(bossGroup, workerGroup)
// 設置要被實例化的爲 NioServerSocketChannel 類
.channel(NioServerSocketChannel.class)
...
}
...
總結
本文主要介紹了Netty
的多線程模型,它採用的是Reactor
模型。處理連接請求與處理IO
操作的線程隔離。基於事件輪詢監聽,不斷獲取處於就緒狀態的通道。其中Boss
線程池的線程負責處理連接請求,接收到accept事件之後,將對應的socket
進行封裝生成NioSocketChannel
對象,並將其提交到workBoss
線程池中,處理IO的read
以及write
事件。