Reactor 线程模型
由于传统 的阻塞 IO 对于响应时间不是很好,因此引入了 Reactor
的异步事件模型来提高响应时间。
主要存在以下三种方式:
-
单线程
Reactor
内部通过selector
来监听连接事件,收到事件之后通过dispatcher
来进行分发。如果是连接建立的事件,则由acceptor
进行处理,acceptor
通过接受到连接,创建一个 Handler 来处理后续的事件。 该模型的缺点:由于是使用单线程的方式来处理每个连接事件,因此无法有效地利用 CPU 提供的资源
-
多线程
为了充分利用 CPU 提供的资源,因此使用多线程的方式来对工作集合进行处理。
在主线程中,
Reactor
对象通过selector
来监控连接事件,收到事件之后通过dispatch
进行分发,如果是连接事件,则由acceptor
进行处理,acceptor
通过接收到连接,创建一个Handler
来处理后续的事件。在这个线程模型中,该Handler
只是负责响应事件,不进行任何业务操作,所有的业务操作都放在线程池中进行处理。 该模型存在的缺点:在同时接受大量的连接请求时,由于所有的连接请求都是通过单个的
Reactor
对象来进行处理的,因此这是很可能会造成连接超时的情况。 -
主从多线程
由于使用单独的 Reactor
来处理连接在处理大量连接是可能会导致连接连接超时的情况,因此在这个线程模型中采用了 “主—从” Reactor
的模式来解决多线程模型中出现的问题。现在在这个模型中,存在一个 “主” Reactor
对象,它负责将接收到的请求分发到某个 “从” Reactor
对象,“从” Reactor
对象将会将得到的请求按照 “多线程模型” 的方式对任务进行处理。“从” Reactor
对象的数量由具体的硬件环境来决定。
Netty 线程模型
Netty 通过 NioEventLoopGroup
来实现上文提及的几种 Reactor
模型
-
单线程模型
单线程模型就是只指定一个线程执行客户端的连接和读取操作,即将所有的请求和任务的处理都放在一个线程中执行,只要将
NioEventLoopGroup
中的线程数设置为 1 即可实现单线程模型。NioEventLoopGroup group = new NioEventLoopGroup(1); // 设置 NioEventLoopGroup 线程数为1,将当前的 Reactor 线程模型设置为单线程模型 ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(group) .channel(NioServerSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) // 开启 Nagle 算法 .option(ChannelOption.SO_BACKLOG, 1024) // 最大连接等待队列的长度 .childHandler(new SimpleChannelInboundHandler<SocketChannel>() { @Override protected void channelRead0( ChannelHandlerContext ctx, SocketChannel msg ) throws Exception { /* TODO */ } });
大致的工作流程:
-
多线程模型
多线程模型就是在一个
Reactor
对象中对客户端的连接进行处理,然后将业务交给线程池进行处理。代码如下所示:NioEventLoopGroup group = new NioEventLoopGroup(); ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(group) .channel(NioServerSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .option(ChannelOption.SO_BACKLOG, 1024) .childHandler(new SimpleChannelInboundHandler<SocketChannel>() { @Override protected void channelRead0( ChannelHandlerContext ctx, SocketChannel msg) throws Exception { /* TODO */ } });
值得注意的是,这种模型的实现原理就是将“主”
Reactor
对象和 “从”Reactor
对象设置为同一个NioEventLoopGroup
对象来实现的具体工作流程:
-
主从多线程模型
只要将 “主”
Reactor
对象和 “从”Reactor
对象设置为不同的NioEventLoopGroup
即可达到对应的效果。具体代码如下所示:
NioEventLoopGroup mainGroup = new NioEventLoopGroup(); NioEventLoopGroup minorGroup = new NioEventLoopGroup(); ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(mainGroup, minorGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .option(ChannelOption.SO_BACKLOG, 1024) .childHandler(new SimpleChannelInboundHandler<SocketChannel>() { @Override protected void channelRead0( ChannelHandlerContext ctx, SocketChannel msg ) throws Exception { /* TODO */ } });
工作流程如下所示:
在 Netty 中,“主”
Reactor
实际上依旧只是随机选择一个线程用于处理客户端的连接。与此同时,NioServerSocketChannel
绑定到mainGroup
,而NioSockerChannel
绑定到minorChannel
中
参考:
[1]https://juejin.cn/post/6844903974298976270
[2]http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf