【源码剖析】Netty 服务端 请求处理 详解
前言:
在之前的博文中,本人讲解了 Netty
的 服务端 的 启动流程 的 核心源码
那么,在本篇博文中,本人就来讲解下 Netty
的 服务端 的 请求处理 的 核心源码
连接请求 的 处理:
假设这一时刻,有 客户端 的 连接请求 过来了
就会调用本人上文所讲的 processSelectedKey方法 的逻辑:
我们跟进去:
处理连接 —— read方法:
我们可以看到:
此方法中:
- 将
readBuf
作为 参数,调用了 doReadMessages方法- 对
readBuf
中的每一个 NioSocketChannel,都调用了 NioServerSocketChannel 的 pipeline 中的 每一个Handler 的 channelRead处理逻辑
(由于当前状态,是 通道初始化完毕,
因此 NioServerSocketChannel 的 pipeline 的 结构 如下:
)
我们继续跟进去:
建立连接 —— doReadMessages方法:
我们可以看到:
内部执行逻辑,只是:
- 与 客户端 建立 连接通道
- 将 上一步建立的 连接通道,封装成
NioSocketChannel
类型,放入readBuf
中
我们来看看上图中 SocketUtils.accept方法 的 实现逻辑:
NIO 原生API调用 —— SocketUtils.accept方法:
我们可以看到:
此方法 中,调用了
NIO
的 原生API,实现了 与客户端 连接通道 的 建立
那么,我们再来看看,封装Channel 的 NioSocketChannel 双参构造器,实现了 什么逻辑:
封装Channel —— NioSocketChannel 双参构造:
跟进 父类构造器:
我们可以看到:
此方法,同样调用了 父类构造器
但是,第三个参数,是NIO
的 原生API 的 读事件注册Key
想必之后的逻辑,会对当前建立的通道,注册 读事件
我们继续跟进:
我们可以看到:
内部执行逻辑,只是:
- 将 与客户端建立的通道,赋值给
ch
属性- 将 读事件注册Key,赋值给
readInterestOp
属性- 设置 当前通道 为 非阻塞
但是,并没有,注册 读事件!
在下文中,会使用到 本方法 中 设置的 两个成员属性,来 注册通道 和 对应事件
在 连接建立 完毕 之后,我们来看看还执行了什么逻辑:
channelRead逻辑调用 —— invokeChannelRead方法:
我们可以看到:
内部执行逻辑,只是:
- 调用 AbstractChannelHandlerContext 的 invokeChannelRead方法
自身参数,就是 调用 invokeChannelRead方法 的 第二参数
(也就是说:所传参数,就是 channelRead处理逻辑,所 读取到的 msg)
那么,我们继续跟下去,来验证下本人的讲解:
我们继续跟下去:
可以看到:
在本方法中,以 责任链模式,调用了 pipeline 中的 所有处理器 的 channelRead方法
验证了本人的讲解!
因此,接下来会以 加入 readBuf
的 NioSocketChannel
作为 msg
(是的,你没有看错,就是以 NioSocketChannel 作为 msg),
调用 ServerBootstrapAcceptor
的 channelRead方法:
我们可以看到:
内部执行逻辑,只是:
- 将 之前用户自定义的 ChannelInintializer处理器逻辑,加入 每一个与客户端建立的 NioSocketChannel 的 pipeline 中
- 向
workerGroup
上 注册 所有NioSocketChannel
当前方法执行完毕后,相应的 NioSocketChannel 的 pipeline
就会变为 如下格式:
(注意:此时,所有NioSocketChannel
均未初始化,之前 初始化过 的只是 NioServerSocketChannel
)
注册通道:
至于 NioSocketChannel
是如何 注册到 workerGroup
上的,
和之前博文《【源码剖析】Netty 服务端 启动流程 详解》中,向 bossGroup
注册 ServerSocketChannel
通道 的逻辑,是基本上一样的
本人就不重复进行讲解了,只是简单说下 实现逻辑
:
- 将
NioSocketChannel
注册到workerGruop
的任一NioEventLoop
上的Selector
上
且 注册事件 为读事件
- 将 用户通过 childHandler方法 传入的 ChannnelInintializer 中的 initChannel方法 的逻辑,也执行一遍
(一般都是 向 NioSocketChannel 中的pipeline
中 添加 新的Handler)
并将 ChannnelInintializer 从pipeline
中 移除
最终,每一个 NioSocketChannel 中的 pipeline
,结构 如下:
客户端消息 的 处理:
假设这一时刻,有 客户端 的 消息 过来了
还是会调用本人上文所讲的 processSelectedKey方法 的逻辑:
那么,我们继续跟进去,就会发现:
连接事件 和 消息处理事件 的 read方法 实现类 不一样
处理消息 —— read方法:
我们可以看到:
内部执行逻辑,只是:
- 申请 缓冲区 空间
- 将 客户端数据 读入 byteBuf 中
- 责任链模式,调用 用户自定义处理器 的 channelRead 和 channelReadComplete 的逻辑
那么,本人再来讲解下 doReadBytes方法 的 执行逻辑:
消息读取 —— doReadBytes方法:
我们可以看到:
内部执行逻辑,只是:
- 调用 ByteBuf 的 API,通过 NioSocketChannelChannel,
将 客户端消息 读入 ByteBuf 中
至此,Netty
的 服务端 的 请求处理 的 核心源码,就讲解完毕了!