實際上Netty線程模型就是Reactor模式的一個實現,而Reactor模式又是什麼呢?
Reactor模型
Reactor模式是基於事件驅動開發的,核心組成部分包括Reactor和線程池,其中Reactor負責監聽和分配事件,線程池負責處理事件,而根據Reactor的數量和線程池的數量,又將Reactor分爲三種模型:
- 單線程模型 (單Reactor單線程)
- 多線程模型 (單Reactor多線程)
- 主從多線程模型 (多Reactor多線程)
單Reactor單線程模型
從圖中可以看出:
它是由一個線程來接收客戶端的連接,並將該請求分發到對應的事件處理 handler 中
這種模型好處是簡單,壞處卻很明顯,當某個Handler阻塞時,會導致其他客戶端的handler和accpetor都得不到執行,無法做到高性能,只適用於業務處理非常快速的場景
單Reactor多線程模型
該模型在事件處理器(Handler)部分採用了多線程(線程池)
相對於第一種模型來說,在處理業務邏輯,也就是獲取到IO的讀寫事件之後,交由線程池來處理,handler收到響應後通過send將響應結果返回給客戶端。這樣可以降低Reactor的性能開銷,從而更專注的做事件分發工作了,提升整個應用的吞吐。
存在的問題:
Reactor承擔所有事件的監聽和響應,只在主線程中運行,可能會存在性能問題。例如併發百萬客戶端連接,或者服務端需要對客戶端握手進行安全認證,但是認證本身非常損耗性能。
於是又有了下面的線程模型。
主從Reactor多線程模型
比起第二種模型,它是將Reactor分成兩部分:
- mainReactor負責監聽server socket,用來處理網絡IO連接建立操作,將建立的socketChannel指定註冊給subReactor。
- subReactor主要做和建立起來的socket做數據交互和事件業務處理操作。通常,subReactor個數上可與CPU個數等同。
Netty的線程模型
通過配置boss和worker線程池的線程個數以及是否共享線程池等方式,Netty的線程模型可以在以上三種Reactor模型之間進行切換
/**
* 單Reactor單線程模型
*/
public class SingleReactorDemo {
public static void main(String[] args) {
NioEventLoopGroup group = new NioEventLoopGroup(1);
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(group)//單線程處理
.channel(NioServerSocketChannel.class)
.childHandler(new ServerHandler());
serverBootstrap.bind(8000);
}
}
/**
* 單Reactor多線程模型
*/
public class SingleReactorMoreThreadDemo {
public static void main(String[] args) {
NioEventLoopGroup group = new NioEventLoopGroup(1);
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(group)//單線程處理
.channel(NioServerSocketChannel.class)
.childHandler(new MoreThreadHandler());
serverBootstrap.bind(8000);
}
}
/**
* 多線程Handler
*/
public class MoreThreadHandler extends ChannelInboundHandlerAdapter {
private static ExecutorService executors = Executors.newScheduledThreadPool(200);
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//讀取數據
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
executors.submit(()->{
//向客戶端寫數據
String currentTime = new Date(System.currentTimeMillis()).toString();
ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
ctx.write(resp);
ctx.flush();//刷新後纔將數據發出到SocketChannel
});
}
}
/**
* 主從Reactor多線程模型
*/
public class MoreReactorMoreThreadDemo {
public static void main(String[] args) {
// bossGroup表示監聽端口,accept 新連接的線程組,workerGroup表示處理每一條連接的數據讀寫的線程組
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new MoreThreadHandler());
serverBootstrap.bind(8000);
}
}