Netty应用(六) ---- netty源码(待完善)

2023-12-17 22:23:18

1.启动流程

1.1 回顾nio中的启动流程

//1 netty 中使用 NioEventLoopGroup (简称 nio boss 线程)来封装线程和 selector
Selector selector = Selector.open(); 

//2 创建 NioServerSocketChannel,同时会初始化它关联的 handler,以及为原生 ssc 存储 config
NioServerSocketChannel attachment = new NioServerSocketChannel();

//3 创建 NioServerSocketChannel 时,创建了 java 原生的 ServerSocketChannel,serverSocketChannel的作用可以理解为一个注册器,netty中的ssc作为附件注册到原生的ssc上
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); 
serverSocketChannel.configureBlocking(false);

//4 启动 nio boss 线程执行接下来的操作

//5 注册(仅关联 selector 和 NioServerSocketChannel),未关注事件
SelectionKey selectionKey = serverSocketChannel.register(selector, 0, attachment);

//6 head -> 初始化器 -> ServerBootstrapAcceptor -> tail,初始化器是一次性的,只为添加 acceptor

//7 绑定端口
serverSocketChannel.bind(new InetSocketAddress(8080));

//8 触发 channel active 事件,在 head 中关注 op_accept 事件
//表示当客户端接入的时候触发这个事件
selectionKey.interestOps(SelectionKey.OP_ACCEPT);

对于Nio中的启动流程,主要分为以下几步:

  1. 创建selector选择器 -> Selector selector = Selector.open();选择器是在new NioEventLoopGroup() 创建好的,EventLoop 里面是维护了一个选择器和一个线程。这块在第二节介绍。
  2. 创建java原生的SSC -> ServerSocketChannel.open();
  3. 通过注册,将selector与NioSSC关联起来,用于后续线程的切换 -> ssc.register(selector, 0, attachment);
  4. 绑定端口 -> bind(new InetSocketAddress(“localhost”, 8080));
  5. 监听事件,这里指Accept事件 -> key.interestOps(SelectionKey.OP_ACCRPT);

debug代码

public class AllocServer {
    public static void main(String[] args) {
        new ServerBootstrap().group(new NioEventLoopGroup())
            .channel(NioServerSocketChannel.class)
            .childHandler(new ChannelInitializer<NioSocketChannel>() {
                protected void initChannel(NioSocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                        @Override
                        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                            ByteBuf buffer = ctx.alloc().buffer();
                            System.out.println(buffer.getClass());
                        }
                    });
                }
            })
            .bind(8080);
    }
}

我们将断点打到bind方法,debug类型设置为Thread并点击make default作为默认类型。
在这里插入图片描述

1.2 initAndRegister()

我们看下bind方法的内部调用链,找到doBind方法。
在这里插入图片描述

1.2.1 初始化

在initAndRegister()方法中,我们先看初始化的逻辑,即

  channel = channelFactory.newChannel();
  init(channel);

1.2.2 注册

代码

ChannelFuture regFuture = config().group().register(channel);

1、注册的代码层级比较深,这里先标一下层级调用链。
在这里插入图片描述
2、注册核心逻辑
调起Nio线程执行注册的核心逻辑register0()方法。
在这里插入图片描述
debug线程也切换到NioEventLoop线程。
在这里插入图片描述

doRegister()方法
在这里插入图片描述
pipeline.invokeHandlerAddedIfNeeded()方法
在执行完doRegister()方法方法后,在invokeHandlerAddedIfNeeded中会回调initAndRegister()方法中添加的处理器中的initChannel方法,给pipeline上又添加了一个ServerBootstrapAcceptor处理器。
在这里插入图片描述
safeSetSuccess(promise):将promise对象中的结果设置为success,表明已经完成注册

1.2.3 dobind绑定

我们去掉前面的断点,然后将断点打到doBind0方法上。
在这里插入图片描述
bind方法的层级比较深,建议通过debug模式往下看,不要自己点。我这里把流程标一下。
在这里插入图片描述
最终,核心代码在AbstractChannel.java中的bind方法。里面有两个重要的方法,如下图。
在这里插入图片描述
在这里插入图片描述

1.2.4 小结

  1. init 和 register regFuture 处理
  • init (main线程执行)
    a. 创建 NioServerSocketChannel (main线程执行)
    b. 添加 NioServerSocketChannel 初始化 handler (main线程执行)
    初始化 handler等待调用 (main未调用)
    向 nio ssc 中加入了 accept handler(在 accept 事件发生后建立连接)
  • register(切换线程)
    a. 启动 nio boss 线程(main线程执行)
    b. 原生 ssc(ServerSocketChannel) 注册到 selector 未关注事件(nio-thread执行)
    c. 执行 NioServerSocketChannel 初始化 handler(nio-thread执行)
  1. regFuture 等待回调 doBind0
    原生 ServerSocketChannel绑定(nio-thread执行)
    触发 NioServerSocketChannel active 事件(nio-thread执行)

2.eventLoop

3.accept流程

4.read

文章来源:https://blog.csdn.net/qq_45268591/article/details/134974675
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。