前言
netty学习系列笔记总结,接受请求过程源码浅析,错误之处欢迎指正, 共同学习
背景
netty启动过程源码分析,我们得知,服务器最终注册了一个 Accept 事件等待客户端的连接。我们也知道,NioServerSocketChannel 将自己注册到了 boss 单例线程池(reactor 线程)上,也就是 EventLoop.
EventLoop所做的事情均分为以下三个步骤
1.轮询注册在selector上的IO事件
2.处理IO事件
3.执行异步task
新链接的建立
- 检测到有新的连接
- 将新的连接注册到worker线程组
- 注册新连接的读事件
1.检测新连接
- 进入到 NioEventLoop 源码中,找到 processSelectedKey()方法设置断点
- debug 启动 EchoServer 的 main 方法
- 建立一个新的连接:telnet 127.0.0.1 8888

上面代码表示boos reactor线程已经轮询到 SelectionKey.OP_ACCEPT 事件,说明有新的连接进入,此时将调用channel的 unsafe来进行实际的操作。接下来,进入到它的read方法,进入新连接处理的第二步
2.注册到reactor线程

1 | 1.检查该 eventloop 线程是否是当前线程。 |
进入 doReadMessages 方法

netty调用jdk底层 javaChannel().accept();由于netty中reactor线程第一步就扫描到有accept事件发生,因此,这里的accept方法是立即返回的,返回jdk底层nio创建的一条channel
netty将jdk的 SocketChannel 封装成自定义的 NioSocketChannel,添加到容器中,做后续处理

默认一次最多读取16条连接
创建NioSocketChannel
1 |
|
该方法调用了 NioServerSocketChannel 中的 serverSocketChannel.accept() 方法。返回了一个 Nio 的通道,注意:这个通道,就是我们刚刚 Boss 线程监听到的 Accept 事件,相当于一个 Tcp 连接。
然后我们看 NioSocketChannel 的创建过程,其中参数 this 是 NioServerSocketChannel ,这个就是 SocketChannel 的 parent 属性,ch 是 SocketChannel 。构造方法如下:
1 | public NioSocketChannel(Channel parent, SocketChannel socket) { |
Netty中的Channel的分类

1.Unsafe[实现Channel读写抽象],服务端NioMessageUnsafe读连接,客户端NioByteUnsafe读数据
2.AbstractChannel用于实现channel的大部分方法,其中我们最熟悉的就是其构造函数中,创建出一条channel的基本组件
3.AbstractNioChannel基于AbstractChannel做了nio相关的一些操作,保存jdk底层的 SelectableChannel,并且在构造函数中设置channel为非阻塞
4.最后,就是两大channel,NioServerSocketChannel,NioSocketChannel对应着服务端接受新连接过程和新连接读写过程,分别创建NioServerSocketChannelConfig,NioSocketChannelConfig
在创建出一条 NioSocketChannel之后,放置在List容器里面之后,就开始进行下一步操作
1 |
|
循环执行 pipeline.fireChannelRead 方法
DefaultChannelPipeline.fireChannelRead(NioSocketChannel)
1 | // 这里的Pipeline是NioServerSocketChannel的Pipeline, 此时Pipeline中有一个ServerBootstrapAcceptor, 所以会传递到ServerBootstrapAcceptor的channelRead方法 |
ServerBootstrapAcceptor.channelRead(ctx, msg)
1 |
|
拿到该channel,也就是我们之前new出来的 NioSocketChannel对应的pipeline,将用户代码中的
childHandler,添加到pipeline,然后进入到 childGroup.register(child),这里的childGroup就是我们在启动代码中new出来的NioEventLoopGroup。之后, 触发channelRegister事件, 执行ChannelInitializer的initChannel方法, 进一步初始化NioSocketChannel所对应的pipeline.
至此, 接收客户端并注册到NioEventLoop的过程完毕。
接下来重点看下childGroup.register方法,为什么是subReactor
1 | // next方法使用位运算获取数组中的EventLoop |
pipeline.invokeHandlerAddedIfNeeded();
1 | // 该方法最终会调用到用户的initChannel方法 |
3.注册读事件
1 | private void register0(ChannelPromise promise) { |
总结
1.boos reactor线程轮询到有新的连接进入
2.通过封装jdk底层的channel创建 NioSocketChannel以及一系列的netty核心组件
3.将该条连接通过chooser,选择一条worker reactor线程绑定上去
4.注册读事件,开始新连接的读写
问:Netty是在哪里检测有新连接接入的?
答:Boss线程通过服务端Channel绑定的Selector轮询OP_ACCEPT事件,通过JDK底层Channel的accept()方法获取JDK底层SocketChannel创建新连接
问:新连接是怎样注册到NioEventLoop线程的?
答:Worker线程调用Chooser的next()方法选择获取NioEventLoop绑定到客户端Channel,使用doRegister()方法将新连接注册到NioEventLoop的Selector上面
Netty新连接接入处理逻辑:
服务端Channel绑定的NioEventLoop即Boss线程轮询OP_ACCEPT事件,调用服务端Channel的accept()方法获取客户端Channel封装成NioSocketChannel,
封装创建组件Unsafe用来实现Channel读写和Pipeline负责数据处理业务逻辑链,服务端Channel通过连接接入器ServerBootstrapAcceptor给客户端Channel分配NioEventLoop,
将客户端Channel绑定到Selector上面,通过传播Channel Active方法将客户端Channel读事件注册到Selector