目录
前置知识:
Java 4种IO模型
网络模型和TCP协议
Reactor线程模型
Reactor线程模型不是Java专属,也不是Netty专属,它其实是一种并发编程模型,是一种思想,具有指导意义。比如,Netty就是结合了NIO的特点,应用了Reactor线程模型所实现的。 Reactor模型中定义的三种角色:
Reactor:负责监听和分配事件,将I/O事件分派给对应的Handler。新的事件包含连接建立就绪、 读就绪、写就绪等。
Acceptor:处理客户端新连接,并分派请求到处理器链中。
Handler:将自身与事件绑定,执行非阻塞读/写任务,完成channel的读入,完成处理业务逻辑 后,负责将结果写出channel。
常见的Reactor线程模型有三种,如下:
Reactor单线程模型
Reactor多线程模型
主从Reactor多线程模型
Reactor单线程模型:
Reactor充当多路复用器角色,监听多路连接的请求,由单线程完成
Reactor收到客户端发来的请求时,如果是新建连接通过Acceptor完成,其他的请求由Handler完成。
Handler完成业务逻辑的处理,基本的流程是:Read --> 业务处理 --> Send 。
优点
结构简单,由单线程完成,没有多线程、进程通信等问题。
适合用在一些业务逻辑比较简单、对于性能要求不高的应用场景。
缺点
单线程操作,不能充分发挥多核CPU的性能。 当Reactor线程负载过重之后,处理速度将变慢,这会导致大量客户端连接超时
稳定性差,一旦某个业务处理读写或者业务逻辑慢,拖慢整个通信系统
Reactor多线程模型
对单线程模型的改进,业务处理由单独的线程处理
好处是增加了业务处理的吞吐量,但是Reactor还是要处理读写请求,当qps过大时,还是会问题。再进一步改进,就是主从Reactor多线程模型。
主从Reactor多线程模型
主的Reactor新建连接交给Acceptor处理,连接建立后,把channel注册到SubReactor。由SubReactor处理channel的读写请求。
Netty模型
Netty的模型,就是主从Reactor多线程模型
Group就是线程池,BoosGroup处理连接请求,交给Acceptor。Acceptor新建channel后,注册到workGroup。
channel读写事件,由workGroup线程池处理。
netty核心组件
channel
Channel可以理解为是socket连接,在客户端与服务端连接的时候就会建立一个Channel,它负责基本
的IO操作,比如:bind()、connect(),read(),write() 等。 主要作用:
1. 通过Channel可获得当前网络连接的通道状态。
2. 通过Channel可获得网络连接的配置参数(缓冲区大小等)。
3. Channel提供异步的网络I/O操作,比如连接的建立、数据的读写、端口的绑定等。
不同协议、不同的阻塞类型的连接都有不同的 Channel 类型与之对应,常用的 Channel 类型:
NioSocketChannel,NIO的客户端 TCP Socket 连接。 NioServerSocketChannel,NIO的服务器端 TCP Socket 连接。
NioDatagramChannel, UDP 连接。
NioSctpChannel,客户端 Sctp 连接。
NioSctpServerChannel,Sctp 服务器端连接,这些通道涵盖了 UDP 和 TCP 网络 IO 以及文件 IO。
EventLoop、EventLoopGroup
有了 Channel 连接服务,连接之间可以消息流动。如果服务器发出的消息称作“出站”消息,服务器接受
的消息称作“入站”消息。那么消息的“出站”/“入站”就会产生事件(Event)。 例如:连接已激活;数据读取;用户事件;异常事件;打开链接;关闭链接等等。
有了事件,就需要一个机制去监控和协调事件,这个机制(组件)就是EventLoop。
在 Netty 中每个 Channel 都会被分配到一个 EventLoop。一个 EventLoop 可以服务于多个 Channel。
每个 EventLoop 会占用一个 Thread,同时这个 Thread 会处理 EventLoop 上面发生的所有 IO 操作和 事件。
ChannelHandler
ChannelHandler对使用者而言,可以说是最重要的组件了,因为对于数据的入站和出站的业务逻辑的
编写都是在ChannelHandler中完成的。 在前面的例子中,MyChannelHandler就是实现了channelRead方法,获取到客户端传来的数据。 对于数据的出站和入站,有着不同的ChannelHandler类型与之对应:
ChannelInboundHandler 入站事件处理器
ChannelOutBoundHandler 出站事件处理器
ChannelInboundHandlerAdapter 与 SimpleChannelInboundHandler的区别:
在服务端编写ChannelHandler时继承的是ChannelInboundHandlerAdapter 在客户端编写ChannelHandler时继承的是SimpleChannelInboundHandler 两者的区别在于,前者不会释放消息数据的引用,而后者会释放消息数据的引用。
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
boolean release = true;
try {
if (acceptInboundMessage(msg)) {
@SuppressWarnings("unchecked")
I imsg = (I) msg;
channelRead0(ctx, imsg);
} else {
release = false;
ctx.fireChannelRead(msg);
}
} finally {
if (autoRelease && release) {
ReferenceCountUtil.release(msg);
}
}
}
ChannelPipeline
在Channel的数据传递过程中,对应着有很多的业务逻辑需要处理,比如:编码解码处理、读写操作 等,那么对于每种业务逻辑实现都需要有个ChannelHandler完成,也就意味着,一个Channel对应着 多个ChannelHandler,多个ChannelHandler如何去管理它们,它们的执行顺序又该是怎么样的,这就 需要ChannelPipeline进行管理了。
一个Channel包含了一个ChannelPipeline,而ChannelPipeline中维护了一个ChannelHandler的列 表。
ChannelHandler与Channel和ChannelPipeline之间的映射关系,由ChannelHandlerContext进行维 护。
ChannelHandler按照加入的顺序会组成一个双向链表,入站事件从链表的head往后传递到最后一个 ChannelHandler,出站事件从链表的tail向前传递,直到最后一个ChannelHandler,两种类型的 ChannelHandler相互不会影响。
Bootstrap
Bootstrap是引导的意思,它的作用是配置整个Netty程序,将各个组件都串起来,最后绑定端口、启动
Netty服务。 Netty中提供了2种类型的引导类,一种用于客户端(Bootstrap),而另一种(ServerBootstrap)用于服务
器。 它们的区别在于:
ServerBootstrap 将绑定到一个端口,因为服务器必须要监听连接,而 Bootstrap 则是由想要连接 到远程节点的客户端应用程序所使用的。
引导一个客户端只需要一个EventLoopGroup,但是一个ServerBootstrap则需要两个。
因为服务器需要两组不同的 Channel
第一组将只包含一个 ServerChannel,代表服务器自身的已绑定到某个本地端口的正在监听 的套接字。
第二组将包含所有已创建的用来处理传入客户端连接。
与ServerChannel相关联的EventLoopGroup 将分配一个负责为传入连接请求创建 Channel 的 EventLoop。一旦连接被接受,第二个 EventLoopGroup 就会给它的 Channel 分配一个 EventLoop。