深入理解 Netty-Channel架构体系 (三)

简介: 深入理解 Netty-Channel架构体系 (三)

递进AbstractNioChannel#



跟进构造方法#


依然是来到AbstractNioChannel的构造方法,发现它做了如下的构造工作:

  • 把parent传递给了AbstractChannel
  • 把子类传递过来的Channel要告诉Selector的感兴趣的选项保存
  • 设置channel为非阻塞


// todo 无论是服务端的channel 还是客户端的channel都会使用这个方法进行初始化
// // TODO: 2019/6/23                null        ServerSocketChannel       accept
// todo  如果是在创建NioSocketChannel  parent==NioServerSocketChannel  ch == SocketChanel
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
    super(parent);// todo  继续向上跟,创建基本的组件
    // todo 如果是创建NioSocketChannel   这就是在保存原生的jdkchannel
    // todo 如果是创建NioServerSocketChannel   这就是在保存ServerSocketChannel
    this.ch = ch;
    // todo 设置上感兴趣的事件
    this.readInterestOp = readInterestOp;
    try {
        // todo 作为服务端, ServerSocketChannel 设置为非阻塞的
        // todo 作为客户端   SocketChannel 设置为非阻塞的
        ch.configureBlocking(false);
    } catch (IOException e) {


重写了它父类的doRegister()#


AbstractNioChannel维护channel的引用,真正的实现把 jdk 原生的 channel注册进 Selector中


@Override
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
    // todo  javaChannel() -- 返回SelectableChanel 可选择的Channel,换句话说,可以和Selector搭配使用,他是channel体系的顶级抽象类, 实际的类型是 ServerSocketChannel
    // todo  eventLoop().unwrappedSelector(), -- >  获取选择器, 现在在AbstractNioChannel中 获取到的eventLoop是BossGroup里面的
    // todo  到目前看, 他是把ServerSocketChannel(系统创建的) 注册进了 EventLoop的选择器
    // todo 这里的 最后一个参数是  this是当前的channel , 意思是把当前的Channel当成是一个 attachment(附件) 绑定到selector上 作用???
    // todo  现在知道了attachment的作用了
     //    todo 1. 当channel在这里注册进 selector中返回一个selectionKey, 这个key告诉selector 这个channel是自己的
     //    todo 2. 当selector轮询到 有channel出现了自己的感兴趣的事件时, 需要从成百上千的channel精确的匹配出 出现Io事件的channel,
    //     todo     于是seleor就在这里提前把channel存放入 attachment中, 后来使用
    // todo 最后一个 this 参数, 如果是服务启动时, 他就是NioServerSocketChannel   如果是客户端他就是 NioSocketChannel
    // todo 到目前为止, 虽然注册上了,但是它不关心任何事件
    selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
    return;
} catch (CancelledKeyException e) {


新增内部接口#


AbstractNioChannel新添加了一个内部接口,作为原Channel的扩展,源码如下, 我们着重关心的就是这个新接口的 read()方法, 它的作用是从channel去读取IO数据,作为接口的抽象方法,它规范服务端和客户端根据自己需求去不同的实现这个read()

怎么特化实现这个read方法呢? 若是服务端,它read的结果就是一个新的客户端的连接, 如果是客户端,它read的结果就是 客户端发送过来的数据,所以这个read()很有必要去特化


/**
 * Read from underlying {@link SelectableChannel}
 */
// todo 两个实现类, NioByteUnsafe , 处理关于客户端发来的信息
// todo NioMessageUnsafe   处理客户端新进来的连接
void read();
/**
* Special {@link Unsafe} sub-type which allows to access the underlying {@link SelectableChannel}
*/
public interface NioUnsafe extends Unsafe {
/**
 * Return underlying {@link SelectableChannel}
 */
SelectableChannel ch();
/**
 * Finish connect
 */
void finishConnect();
void forceFlush();
}


AbstractNioChannel抽象内部内同类时继承了它父类的AbstractUnsafe实现了当前的NioUnsafe, 再往后看, 问题来了, 服务端和客户端在的针对read的特化实现在哪里呢? 想想看肯定在它子类的unsafe内部类中,如下图,紫框框



一会再具体看这两个 内部类是如何特化read的 注意啊,不再是抽象的了


再进一步 AbstractNioMessageChannel#


它的构造函数如下, 只是调用父类的构造函数,传递参数


protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
    // todo 在进去
    // todo  null  ServerSocketChannel   accept
    super(parent, ch, readInterestOp);
}


AbstractNioMessageChannelMessageNioUnsaferead()特化实现#


在read方法中,我们可以看到,他调用是本类的抽象方法doReadMessages(List<Object> buf), 方法的实现类是继承体系的最底层的NioServerSocketChannel, 因为他就是那个特化的服务端channel

当然如果我们一开始跟进read()时,来到的客户端的AbstractNioByteChannel,现在我们找到的doReadMessage()就是由 客户端的channelNioSocketChannel完成的doReadBytes()


// todo 用于处理新链接进来的内部类
private final class NioMessageUnsafe extends AbstractNioUnsafe {
// todo 这个容器用于存放临时读到的连接
private final List<Object> readBuf = new ArrayList<Object>();
// todo 接受新链接的 read来到这里
@Override
public void read() {
    ...
    doBeginRead(buf);
    ...
}
// todo 处理新的连接 是在 NioServerSocketChannel中实现的, 进入查看
protected abstract int doReadMessages(List<Object> buf) throws Exception;


最终,特化的channel实现#


现在我们就来到了最底层,整张继承图就全部展现在眼前了,下面就去看看,特化的服务端Channel NioServerSocketChannelNioSocketChanneldoReadMessages()doReadBytes()的各自实现

服务端, 我们看到了,它的特化read()是在创建新的 Jdk远程channel, 因为它在创建新的连接chanel


@Override
protected int doReadMessages(List<Object> buf) throws Exception {
    // todo java Nio底层在这里 创建jdk底层的 原生channel
    SocketChannel ch = SocketUtils.accept(javaChannel());
    try {
        if (ch != null) {
            // todo  把java原生的channel, 封装成 Netty自定义的封装的channel , 这里的buf是list集合对象,由上一层传递过来的
            // todo  this  --  NioServerSocketChannel
            // todo  ch --     SocketChnnel
            buf.add(new NioSocketChannel(this, ch));
            return 1;
        }
        ...


客户端, 读取客户端发送过来的IO数据


@Override
protected int doReadBytes(ByteBuf byteBuf) throws Exception {
    final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
    allocHandle.attemptedBytesRead(byteBuf.writableBytes());
    return byteBuf.writeBytes(javaChannel(), allocHandle.attemptedBytesRead());
}


小结:#


Netty的channel继承体系,到现在就完成了, 相信,当我们现在再从 NioServerEventLoop入手,看他的初始化过程应该很简单了, 其中我希望自己可以牢记几个点

  • AbstractChannel维护NioChannelEventLoop
  • AbstractNioChannel维护jdk原生 channel
  • AbstractChannel中的AbstractUnsafe主要是定义了一套模板,给子类提供了填空题,下面的三个填空
  • 注册 把chanel注册进Selector
  • 绑定 把chanel绑定上端口
  • 添加感兴趣的事件, 给创建出来的channel二次注册上netty可以处理的感兴趣的事件
  • channel的io操作是unsafe内部类完成的
  • 服务端从channel,读取出新连接NioMessageUnsafe
  • 客户端从channel,读取出数据NioByteUnsafe
相关文章
|
7月前
|
前端开发 网络协议 Dubbo
Netty - 回顾Netty高性能原理和框架架构解析
Netty - 回顾Netty高性能原理和框架架构解析
295 0
|
设计模式 缓存 网络协议
Netty整体介绍和架构认知(一)
Netty整体介绍和架构认知
12366 3
|
编解码 前端开发 网络协议
Netty整体介绍和架构认知(二)
Netty整体介绍和架构认知
176 0
|
存储 安全 大数据
谈谈如何构建现代数据体系架构(数据湖+数据仓库)
如何构建当前企业数据体系架构呢?其实与许多其他技术一样,它实际上取决于企业要实现目标。
谈谈如何构建现代数据体系架构(数据湖+数据仓库)
|
存储 Kubernetes 监控
kubernete架构体系介绍
kubernete架构体系介绍
233 1
kubernete架构体系介绍
|
网络协议 Java 程序员
Netty网络编程(二):架构概述
Netty网络编程(二):架构概述
113 0
|
监控 网络协议 NoSQL
Netty 高性能架构设计
Netty 高性能架构设计
198 0
|
编解码 弹性计算 前端开发
太详细了!终于有人把Netty原理架构讲解清楚了
本文基于 Netty 4.1 展开介绍相关理论模型,使用场景,基本组件、整体架构,知其然且知其所以然,希望给大家在实际开发实践、学习开源项目方面提供参考。
472 1
太详细了!终于有人把Netty原理架构讲解清楚了
|
SQL 存储 缓存
一图搞定MySQL体系架构
要了解mysql的运行机制,那么首先要对mysql的体系结构有一定的了解。
1042 0
一图搞定MySQL体系架构
|
消息中间件 编解码 网络协议
Netty 的三层架构设计
《读尽源码》
308 0