一 Channel和Unsafe的关系
首先这里的Channel是Netty中自定义的类,并不是JDK中提供的java.nio.channels.Channel类,它的全类名是io.netty.channel.Channel。
下面的讨论会同时涉及到这两个类,为了不搞混我们先约定在说Channel时,指的就是Netty中的Channel,而JDK中的Channel,我们用JavaChannel表示。
Channel是一个接口,我们平常使用的是NioServerSocketChannel和NioSocketChannel,它们的继承关系图如下:
NioServerSocketChannel类继承关系图
NioSocketChannel类继承关系图
而Unsafe是也是一个接口,它定义在Channel的内部,是Channel接口的内部接口。
在Channel的具体实现类中,也会有Unsafe的内部实现类。并且Channel中一个字段类型是Unsafe
public abstract class AbstractChannel implements Channel {
private final Unsafe unsafe;
}
因此Channel和Unsafe是属于同一层的东西,我们甚至可以把它们看成一个“组合类”。
Unsafe/Channel层在Netty中负责处理所有与网络相关的事件,包括绑定端口,读写数据,建立/关闭连接。并且还需要向ChannelPipline层通知事件。再贴一下流程图:
image.png
1.1 Unsafe和Channel具体做了什么事?
当外部调用了Unsafe的方法处理某个网络事件时,它会做以下两件事:
- 调用ChannelPipeline发送事件
- 调用Channel中对应的方法做具体的操作
所以说Unsafe主要是负责发送事件给ChannelPipeline,而Channel主要负责具体的操作。
在这种模式下,只要外部是调用了Unsafe中的方法来操作网络事件,那么该事件就一定会被ChannelPipeline处理。
但是有个问题是,实际开发中我们肯定会面向Netty提供的Channel接口做一些具体的操作,例如调用Channel#write()方法,如何确保在这种情况下这个write的数据也会让ChannelPipeline处理呢?
AbstractChannel实现了这个逻辑:
它实现了所有Channel接口中定义的与网络相关的方法,在这些方法中,全都会调用ChannelPipeline对应的方法:
@Override
public ChannelFuture write(Object msg) {
return pipeline.write(msg);
}
注意这些方法不是Pipeline中用于发送事件的方法,它们只是简单的调用了一下Unsafe中的对应方法,画个流程图就是,以write为例:
image.png
二 Unsafe/Channel层处理网络事件的逻辑
其实我觉得吧,弄清楚上面的逻辑,这一层基本就清楚了,但是我还是想具体看看每个网络操作它们各干了什么事。
2.1 服务端绑定端口
服务绑定端口流程整体的流程跟我们在上面分析的是符合的。由于调用的是Channel中的接口,所以会先通过Pipeline把事件转到Unsafe中,再由Unsafe处理。
2.2 服务端接收到ACCEPT事件
- 步骤一:EventLoop收到ACCEPT事件后,调用Unsafe的read()方法(你没看错是read方法)。
- 步骤二:Unsafe先确认NioServerSocketChannel注册的EventLoop已经启动循环select事件了。
- 步骤三:Unsafe调用NioServerSocketChannel的doReadMessages方法做具体的ACCEPT操作
- 步骤四:在NioServerSocketChannel的doReadMessages方法中,调用了JavaChannel的accept方法建立连接。
- 步骤五:连接建立完成后,调用Pipeline发送ChannelRead事件。
- 步骤六:ChannelRead事件发送完成后再调用Pipeline发送ChannelReadComplete事件。
这个事件中,EventLoop直接调用的就是Unsafe的方法,因此直接就可以交到Unsafe处理了。
2.3 服务端收到READ事件后的处理方法
- 步骤一:EventLoop收到READ事件后,调用Unsafe的read()方法。
- 步骤二:Unsafe调用NioSocketChannel的doReadBytes读数据。
- 步骤三:读数据完成后,调用Pipeline发送ChannelRead事件。
- 步骤四:ChannelRead事件发送完成后再调用Pipeline发送ChannelReadComplete事件。
与上面的ACCEPT事件类似的处理逻辑,这里需要注意的是ACCEPT事件和READ事件都调用了Unsafe的read方法处理,但是实际上调用的不是同一个实现类的read方法。
网友评论