美文网首页
JavaNIO(六)Unsafe和Channel解析

JavaNIO(六)Unsafe和Channel解析

作者: 清雨季 | 来源:发表于2019-07-27 16:57 被阅读0次

一 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方法。

相关文章

网友评论

      本文标题:JavaNIO(六)Unsafe和Channel解析

      本文链接:https://www.haomeiwen.com/subject/hlfrrctx.html