一、基本组件与关系
1. 什么是处理器(Handler)?
- Netty 中的“处理器”通常指
ChannelHandler
接口的实现类(可细分为ChannelInboundHandler
与ChannelOutboundHandler
)。 - 一个 Channel 会绑定一个
ChannelPipeline
,pipeline 是由多个 handler(其实是 handler 包装后的ChannelHandlerContext
)组成的链表结构。
2. 当前处理器是啥意思?Netty 中是不是只有一个 WebSocket 服务器?
- WebSocket 是一种协议,而 Netty 是实现 WebSocket 协议的底层框架。
- 所谓“当前处理器”,一般指当前正在调用中的那个
ChannelHandler
,是 Pipeline 中的一个节点。 - 一个 Netty 服务可以有多个 Channel,每个 Channel 对应一个连接,分别绑定自己的 handler 链。
二、ChannelHandlerContext 是什么?怎么来的?
1. ChannelHandlerContext 是怎么生成的?
-
自动生成:当你调用
pipeline.addLast(new MyHandler())
时,Netty 会自动为你包装一个ChannelHandlerContext
。 -
你不需要手动创建它,也不应该去 new 它。
-
但你可以在 handler 方法中拿到它,比如:
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) { // ctx 就是当前 handler 的上下文,供你操作 pipeline }
2. ChannelHandlerContext 是线程安全的吗?
- 它是和当前 handler + 当前 channel 绑定的,不是全局共享。
- 不能随便跨线程调用其他 handler 的 ctx,但可以在业务逻辑中保留 ctx 来回写数据(注意线程安全)。
三、Pipeline 和 Handler 链结构
1. Pipeline 是什么?
-
ChannelPipeline 是一条双向链表:
headContext <-> handler1 <-> handler2 <-> ... <-> tailContext
-
每个 handler 被包裹在一个 ChannelHandlerContext 中,彼此通过 next/prev 指针连接。
2. pipeline.addLast(new MyHandler()) 是什么意思?
- 是你手动添加 handler 的方式。
- 你写的 MyHandler 通常继承自
ChannelInboundHandlerAdapter
或ChannelOutboundHandlerAdapter
,或者实现ChannelInboundHandler
接口。
四、事件触发机制详解
1. 什么叫事件?什么叫触发?
- 事件是 Netty 的数据流或状态变化的抽象,比如:
- channelRegistered(注册到 selector)
- channelActive(连接建立)
- channelRead(收到数据)
- exceptionCaught(异常)
- write、flush(输出数据)
- 这些事件会触发 pipeline 中相应的 handler 方法。
2. fireXXX 是什么?
- fire = “触发”,不是“炒鱿鱼”😂
ctx.fireChannelRead(msg)
表示当前 handler 处理完了,要把事件继续传给下一个 handler。- Netty 内部机制会从当前
ChannelHandlerContext
开始,依次调用下一个 context 的相应方法。
3. fireChannelRead 与 channelRead 区别?
fireChannelRead()
是传播事件的函数(跳过当前 ctx,往后传播)。channelRead()
是事件处理函数,是你在 handler 中实现的逻辑代码。
五、handler 中的方法调用
1. handler 中怎么知道调用哪个方法?
- Netty 会根据事件类型自动调用:
- 连接建立:调用
channelActive()
- 注册 selector:
channelRegistered()
- 收到数据:
channelRead()
- 异常处理:
exceptionCaught()
- 写数据:
write()
- flush 数据:
flush()
- 用户事件:
userEventTriggered()
- 连接建立:调用
六、关于 flush 与 write 的区别
write()
只是把数据写入缓存,并不真正发送。flush()
才是触发真正发送数据的操作。- 类似 Java 的输出流
writer.write()
vswriter.flush()
。
七、ChannelHandlerContext 和用户账号的绑定
ChannelHandlerContext channel = nettyWebSocketDeviceChannelCacheManager.getLoginWatchmanChannel(account);
Attribute<Kv> attr = channel.channel().attr(AttributeKey.valueOf("userInfo"));
attr.set(kv);
分析:
channel.channel()
是从 ctx 中取出底层的 Channel。.attr(...)
是 Netty 提供的机制:给 channel 设置属性值(Attribute),相当于一个线程安全的Map<AttributeKey, Object>
。- 所以你可以把用户登录信息(account)绑定到对应连接的 channel 上。
是不是跟 account 绑定了?
是的。你的 account → ChannelHandlerContext,通过 map 缓存绑定住。
八、userEventTriggered 是什么?
-
这是 Netty 的自定义事件机制。
-
你可以自己通过
ctx.fireUserEventTriggered(event)
来触发某个事件,然后在 handler 中重写:@Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof IdleStateEvent) { // 处理心跳超时等 } }
九、handler 是否一定调用全部?
回答:
- 入站事件(如 channelRead)从
head
→tail
顺序传播; - 出站事件(如 write、flush)从
tail
→head
反向传播; - 如果中间 handler 不调用
ctx.fireChannelRead(msg)
,后面的 handler 就接收不到这个事件了!
十、pipeline 只调用某个 handler,可以吗?
- 你无法直接调用某个中间 handler 的
channelRead()
方法。 - Netty 的事件机制是从当前 ctx 往后传播的(你可以中断,但不能跳着调用)。
- 如果你要特殊调用某个 handler,只能在 handler 内部写逻辑判断,例如基于 msg 类型或业务字段。
十一、MyChannelInitializer 需要 @bean 吗?
- 如果你在 Spring Boot 中使用 Netty,并希望通过自动配置注入 pipeline,那就需要通过
@Configuration
和@Bean
。 - 否则在原生 Netty 使用场景下,只要你手动设置
ServerBootstrap.childHandler(new MyChannelInitializer())
即可。
十二、总结与逻辑梳理
Netty 内部核心机制逻辑流程如下:
1. 初始化阶段
- ServerBootstrap 启动 → childHandler 设置 pipeline → 每个连接创建时执行 ChannelInitializer → 添加 handler
2. 数据接收流程
- socket 接收字节 → 解码器 → fireChannelRead → handler1.channelRead → fireChannelRead → handler2.channelRead → ...
3. 发送数据流程
- handler.write() → fireWrite() 反向传播 → 真正写入 socket buffer
4. 数据绑定逻辑
- channel.attr(...) 可用于保存当前连接相关信息(用户、设备等)
关键点
ChannelHandlerContext
是 handler 在 pipeline 中的上下文封装,Netty 自动创建;pipeline.addLast(...)
是手动添加 handler 的方式;- handler 之间通过
ctx.fireXXX()
进行事件传递; write()
仅写 buffer,flush()
才发出数据;channel.attr(AttributeKey)
可以存放任意信息,用于用户信息绑定;- Netty 是事件驱动的框架,所有处理逻辑都是基于事件触发与传播;
userEventTriggered
是自定义事件的处理入口;- pipeline 是双向链表,支持入站(head→tail)和出站(tail→head)传播;
- handler 方法由事件类型自动触发,不能任意调用 handler 的某个方法;
- 所有 handler 必须显式传播事件,才能让下游 handler 生效。