前言
在上一篇文章中我们详细的走了一遍bind()
方法的启动流程, channel
的初始化和注册, 并在初始化和注册的时候如果是第一次连接的话会执行pipeline.fireChannelActive();
方法进行回调, 这次我们就对回调进行一次详细的讲解
从回调开始
前情回顾
重新带大家回顾一下上一篇文章末尾的知识, 我们进入了AbstractChannel
类的register0
方法, 这个方法是我们的channel
调用注册和回调通知的地方
在register0
方法中重点关注上图中三个红框的地方:
doRegister():
实际注册的方法pipeline.fireChannelRegistered();
回调方法pipeline.fireChannelActive();
tcp 首次连接时调用, 也是本章的重点
tcp客户端首次连接服务端
我以debug
模式启动Netty
项目, 并在该方法上打了断点, 等待一会跳过断点, 可以看到控制台选中部分的时间和上一条打印日志时间间隔近30秒, 在放开debug
之后执行了该方法, 调用回调打印
十二月 07, 2022 10:38:37 上午 io.netty.handler.logging.LoggingHandler channelActive 复制代码
剖析pipeline.fireChannelActive
方法
一路
Ctrl+左键
查看方法详情, 下面注意一下图上的标记类名.方法名
DefaultChannelPipeline.fireChannelActive
AbstractChannelHandlerContext.invokeChannelActive
在该方法中, 我们可以看到不论最后执行结果是什么都会执行一个方法next.invokeChannelActive();
, 那我们就进入这个方法看一下
AbstractChannelHandlerContext.invokeChannelActive
最后可以看到不论怎么执行他都会执行到channelActive()
方法
- 这个方法继承于
ChannelInboundHandlerAdapter
类 ChannelInboundHandlerAdapter
类中的channelActive()
方法又继承于ChannelInboundHandler
接口- 同时, 我们最开始
DefaultChannelPipeline
类下面还写了一个内部类HeadContext
- 在
HeadContext
类中继承了ChannelInboundHandler
也实现了该方法, 咱就说隐藏的真深, 我找半天...如下图
HeadContext
类和channelActive()
方法详情网络异常,图片无法展示|
这个类的继承图如下所示网络异常,图片无法展示|网络异常,图片无法展示|
所以回调到这里就差不多了, 别问我最后怎么理清的, debug
大法好, 可能稍微有点乱, 大家最好自己debug
走一遍, 再挨个类点开看一下
回调详情
DefaultChannelPipeline
**channel.read();**方法是Channel
类的, 因为调用的channel
是NioServerSocketChannel
, 所以我们直接去他的父类AbstractChannel
中查看
NioServerSocketChannel 类的继承图
网络异常,图片无法展示|
从AbstractChannel
的read()
方法进入到DefaultChannelPipeline
的read()
方法
这样我们就找到了真正的read()
方法, 到这里我们继续分析
findContextOutbound()
顾名思义, 这个方法是查找出栈的
这个方法详情流程如下:
- 先是获取当前
ChannelHandler
- 获取当前
ChannelHandler
的executor
- 获取前一个
ChannelHandler
- 判断执行while内的
skipContext
方法 skipContext
方法的作用: 判断前一个ChannelHandler是否具有响应Write事件的资格
executor.inEventLoop()方法
判断当前线程是不是在当前的EventLoop中对应的那个线程
next.invokeRead()
直接进入这个方法查看一下, 神秘的面纱即将揭晓
有没有很熟悉的感觉, 在剖析pipeline.fireChannelActive
方法的时候我们就碰到过类似的方法, 那个是invokeChannelActive()
方法, 也是一个回调
因为handler == headContext, 所以最后就是调用的 handler.read()
方法
回调执行结果
网络异常,图片无法展示|
发现 doBeginRead() 方法
这里注意一下, 看到this.selectionKey
了吗, 如果看我上一篇文章的小伙伴肯定会发现, 这不就是我们之前设置过得值吗
这一步就是重新为我们的selectionKey
进行设置
结尾
本来想完成一下任务三的, 但是实在是搞不动了, 下一篇再说吧...