1.Netty的逻辑架构
如上图逻辑架构参考《Netty权威指南》,Netty也是基于三层模型来实现的
1.1 Reactor通信调度层
&nbs;上图中最底层Reactor层它由一系列辅助类完成,包括Reactor线程NioEventLoop及其父类、NioSocketChannel/它由一系列辅助类完成,包括Reactor线程NioEventLoop及其父类、NioSocketChannel/网络层的数据读取到内存缓冲区中,然后触发各种网络事件,例如连接创建、连接激活、网络层的数据读取到内存缓冲区中,然后触发各种网络事件,例如连接创建、连接激活、的处理。
1.2 责任链Pipeline层
它负责事件在职责链中的有序传播,同时负责动态地编排职责链。职责链可以选择监听和处理自己关心的事件,它可以拦截处理和向后/向前传播事件。不同应用的Handler节点的功能也不同,通常情况下,往往会开发编解码Hanlder用于消息的编解码,它可以将点的功能也不同,通常情况下,往往会开发编解码Hanlder用于消息的编解码,它可以将不需要感知底层的协议差异和线程模型差异,实现了架构层面的分层隔离。
1.3 业务逻辑层Service
这个也是我们开发最需要关心的层,用来和上面的Reactor和Pipeline相互解耦。常见我们的使用某种协议或者定制以及相关业务处理都是操作的这一层。
从上面看除了三层外,发现其实有两条线路:一条用作监听网络;另外一条基于Task做的个调度任务的路线(这个就和我们EventLoop实现有关了,后面详解)
2.Netty的工作架构
如图,这幅图摘于网络,来源后面有附,这是我们常用的netty的工作模型,不过netty也是可以对于这个reactor做相对应的调整。详解reactor模型
2.1 Reactor模型
Reactor 的线程模型有三种:单线程模型、多线程模型、主从多线程模型。
1)单线程模型
首先来看一下单线程模型:
所谓单线程, 即Acceptor 处理和handler 处理都在同一个线程中处理。这个模型的坏处显而易见:当其中某个Handler阻塞时, 会导致其他所有的Client 的Handler 都得不到执行,并且更严重的是,Handler 的阻塞也会导致整个服务不能接收新的Client 请求(因为Acceptor 也被阻塞了)。因为有这么多的缺陷,因此单线程Reactor 模型应用场景比较少。
2)Reactor 多线程模型
那么,什么是多线程模型呢? Reactor 的多线程模型与单线程模型的区别就是Acceptor 是一个单独的线程处理,并且有一组特定的NIO 线程来负责各个客户端连接的IO 操作如下图所示
Reactor 多线程模型有如下特点:
- 1、有专门一个线程,即Acceptor 线程用于监听客户端的TCP 连接请求。
- 2、客户端连接的IO 操作都由一个特定的NIO 线程池负责.每个客户端连接都与一个特定的NIO 线程绑定,因此在这个客户端连接中的所有IO 操作都是在同一个线程中完成的。
- 3、客户端连接有很多,但是NIO 线程数是比较少的,因此一个NIO 线程可以同时绑定到多个客户端连接中。
接下来我们再来看一下Reactor 的主从多线程模型。一般情况下, Reactor 的多线程模式已经可以很好的工作了
3) Reactor 的主从多线程
但是我们想象一个这样的场景:如果我们的服务器需要同时处理大量的客户端连接请求或我们需要在客户端连接时,进行一些权限的校验,那么单线程的Acceptor 很有可能就处理不过来,造成了大量的客户端不能连接到服务器。Reactor 的主从多线程模型就是在这样的情况下提出来的,它的特点是:服务器端接收客户端的连接请求不再是一个线程,而是由一个独立的线程池组成。
其线程模型如下图所示:
可以看到,Reactor 的主从多线程模型和Reactor 多线程模型很类似,只不过Reactor 的主从多线程模型的Acceptor使用了线程池来处理大量的客户端请求。
这里的Reactor模型也是给后面分析NioEventLoop源码打一个铺垫;
Netty可以在启动前通过相关参数控制使用某一个线程模型。
Reactor线程的使用建议:
- (1)创建两个NioEventLoopGroup, 用于逻辑隔离NIO Acceptor和NIO I/O线程。
- (2)尽量不要在ChannelHandler中启动用户线程(解码后用于将POJO消息派发到后端业务线程的除外)。
- (3)解码要放在NIO线程调用的解码Handler中进行,不要切换到用户线程中完成消息的解码。
- (4)如果业务逻辑操作非常简单,没有复杂的业务逻辑计算,没有可能会导致线程被阻塞的磁盘操作、数据库操作、网路操作等,可以直接在NIO线程上完成业务逻辑编排,不需要切换到用户线程。
- (5)如果业务逻辑处理复杂,不要在NIO线程上完成,建议将解码后的POJO消息封装成Task,派发到业务线程池中由业务线程执行,以保证NIO线程尽快被释放,处理其他的I/O操作。
参考:《Netty权威指南》
参考:https://www.cnblogs.com/imstudy/p/9908791.html