前言
Netty 基于 Reactor 模型封装 NIO,屏蔽了 JDK 原生 NIO 繁琐、容易踩坑的底层细节。很多同学初学 Netty 时容易混淆 Channel、ChannelHandler、入站事件、出站事件,而 Channel + HandlerPipeline 这套模型,正是我们实现私有协议、RPC 框架、IM、网关的基础。
一、Channel:网络连接的抽象载体
1. Channel 是什么
Channel 代表一条网络连接,客户端、服务端两侧都存在 Channel 对象,是 Netty 对底层 Socket 的高层抽象,所有 IO 操作都是异步非阻塞。
一句话概括:Channel = 一条异步网络通道,负责读写数据、连接管理、状态维护、配置 Socket 参数。
✅ Channel 具备五大能力:
- 异步读写:异步向远端写入数据、从远端读取数据;write 只是写入缓冲区,需要 flush 才会真正发送
- 连接生命周期管理:打开连接、关闭连接、绑定本地端口、发起远端连接
- Socket 参数配置:设置 TCP_NODELAY、SO_KEEPALIVE、收发缓冲区大小等 Socket 选项
- 状态查询:查询是否打开、是否激活、本地地址、远端地址
- 事件驱动通知:IO 事件会交给绑定在 Channel 上的 Pipeline 中的 Handler 处理
- 自定义属性存储 :
channel.attr(AttributeKey),可以绑定业务自定义对象(比如登录态、会话 ID)
常用场景:attr 非常适合保存会话信息,避免在 Handler 之间传参污染。
2. Channel 核心方法梳理
java
// 获取分配给当前Channel的EventLoop(Reactor线程,一个Channel全程绑定同一个EventLoop)
EventLoop eventLoop();
// 获取当前Channel绑定的Pipeline(责任链,存放所有ChannelHandler)
ChannelPipeline pipeline();
// 判断通道是否激活(TCP连接建立成功,可收发数据)
boolean isActive();
// 获取本地地址
SocketAddress localAddress();
// 获取对端远端地址
SocketAddress remoteAddress();
// 写入数据到缓冲区(异步,不发送)
ChannelFuture write(Object msg);
// 刷缓冲区,把数据真正发送出去
Channel flush();
// write + flush 合并,业务最常用
ChannelFuture writeAndFlush(Object msg);
⚠️ 重点:
writeAndFlush返回 ChannelFuture,是异步操作,不能直接同步拿到结果,需要添加监听器监听发送成功 / 失败。
3. Channel 完整事件生命周期(入站事件)
java
handlerAdded → channelRegistered → channelActive → channelRead → channelReadComplete
→ channelInactive → channelUnregistered → handlerRemoved
逐个说明:
handlerAdded:Handler 被添加到 Pipeline 时触发channelRegistered:Channel 成功注册到 EventLoop(Reactor 线程)channelActive:TCP 连接建立完成,通道激活,可以收发数据(客户端连接成功、服务端接受连接后触发)channelRead:读到远端发来的数据,业务核心处理入口channelReadComplete:本次读操作批量读取完毕,一次网络读事件结束channelInactive:连接断开,通道变成非激活channelUnregistered:Channel 从 EventLoop 注销handlerRemoved:Handler 从 Pipeline 移除
异常事件:
exceptionCaught(Throwable),所有 Handler 都可以重写,捕获 IO 异常、编解码异常。
示意图 1:Channel 生命周期时序图

二、ChannelHandler:事件与数据的处理器
1. ChannelHandler 定位
ChannelHandler 是处理器接口,负责拦截、处理 Channel 上的 IO 事件与数据,是业务逻辑的载体。 所有编解码、协议解析、日志打印、权限校验、业务处理、异常捕获,全部由 Handler 实现。
✅ Handler 能力:
- 数据预处理:入站解码、出站编码
- 事件响应:连接建立、关闭、异常事件响应
- 协议处理:自定义私有协议解析(RPC 报文、IM 消息)
- 切面能力:日志、监控、埋点、限流
- 异常兜底:捕获链路中的异常
2. Handler 三大分类
- ChannelInboundHandler 入站处理器 处理【流向当前 Channel】的事件:远端发来数据、连接建立、连接关闭、读事件等。 方向:远端 → 当前 Channel(读数据)
- ChannelOutboundHandler 出站处理器 处理【从当前 Channel 发往远端】的操作:write、flush、close、connect 等写操作。 方向:当前 Channel → 远端(发数据)
- ChannelDuplexHandler 双工处理器 同时实现入站 + 出站接口,既能处理读事件,也能拦截写事件。适合同时拦截收发场景,比如流量统计、报文加解密。
适配器:
ChannelHandlerAdapter,提供接口的空默认实现,我们开发时继承适配器,只重写需要的方法,不用实现全部抽象方法。
示意图 2:ChannelPipeline 责任链流向图

重点规则: 入站事件:HeadContext → 依次向后传递 出站操作:TailContext → 依次向前传递 如果 handler 中不调用
ctx.fireXXX(),事件会在当前节点截断,后面 handler 收不到事件。
三、Netty 双向通信最简代码示例
场景:客户端连接服务端,连接成功后发送消息;服务端收到消息打印并回复消息。
1. 客户端 Handler(ChannelInboundHandler,重写 channelActive)
客户端:channelActive 代表和服务端 TCP 连接建立成功,此时主动发送消息
java
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
/**
* channelActive:客户端和服务端连接激活成功回调
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("客户端:连接服务端成功,准备发送数据");
// 发送消息并flush
ctx.writeAndFlush("Hello Netty RPC Server!");
super.channelActive(ctx);
}
/**
* channelRead:读取服务端返回的数据
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("客户端收到服务端响应:" + msg);
super.channelRead(ctx, msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
2. 服务端 Handler(ChannelInboundHandler,重写 channelRead)
服务端:channelRead 读取客户端发来的报文,执行业务逻辑,返回结果
java
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
/**
* channelRead:读取客户端发送过来的数据,核心业务处理方法
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String request = (String) msg;
System.out.println("服务端收到客户端消息:" + request);
// 构造响应写回客户端
String resp = "Server Ack: receive your msg -> " + request;
ctx.writeAndFlush(resp);
super.channelRead(ctx, msg);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("服务端:新客户端连接接入,远端地址:" + ctx.channel().remoteAddress());
super.channelActive(ctx);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
补充:生产环境一定需要添加
StringDecoder / StringEncoder或者自定义协议编解码器 Handler 放到 Pipeline,上面示例简化演示回调方法。
四、自研 RPC 架构(基于 Netty Channel + Handler)
我们基于上面 Netty 基础组件,拆解一套轻量 RPC 框架分层结构,核心两个基础抽象:
RpcProtocol:RPC 私有协议,定义报文格式、序列化、编解码逻辑(放在 Pipeline 的 Handler 中)RpcChannel:封装 Netty 原生 Channel,作为 RPC 层面的数据传输通道,封装连接、发送请求、等待响应能力
RPC 整体架构图

分层职责详细说明
客户端模块
- RpcClient:入口类,给业务提供获取远程服务代理的能力,对外屏蔽底层网络
- RpcProxy:动态代理实现。业务调用接口方法时,拦截方法调用,封装为 RpcRequest 对象
- RpcCaller:组装请求报文、生成请求 ID、同步 / 异步等待返回结果,处理响应映射
- RpcConnector:连接管理器,维护长连接 RpcChannel,重连、连接复用、负载均衡
- RpcProtocol:协议编码器,把 RpcRequest 序列化为二进制字节流,交给 Netty Channel 发送
- RpcChannel:对 Netty 原生 Channel 的包装,提供 rpc 层的 send 方法
服务端模块
- RpcServer:RPC 启动入口,注册本地服务接口和实现类映射关系
- RpcAcceptor:基于 Netty,接收客户端连接,收到二进制报文交给协议解码
- RpcProcessor:请求调度中心,管理业务线程池,隔离 IO 线程(EventLoop)和业务线程,防止阻塞 Reactor
- RpcInvoker:反射工具类,根据接口名、方法名、参数,找到本地实现类执行方法,拿到返回结果
- RpcProtocol:协议解码器,将二进制报文反序列化为 RpcRequest,编码 RpcResponse 返回
- RpcChannel:服务端侧包装 Netty Channel,用来回写响应报文
链路一句话:业务调用代理接口 → 封装请求 → 编码 → Netty Channel 发送 → TCP 传输 → 服务端解码 → 反射调用本地方法 → 结果编码返回客户端
五、核心踩坑
- EventLoop 是单线程!同一个 Channel 所有事件由同一个线程处理,Handler 里面不能执行耗时阻塞业务,否则卡死整个 Reactor,业务必须丢到独立线程池(对应 RPC 的 RpcProcessor)
channel.write()不会发送数据,必须 flush;优先用 writeAndFlush- Pipeline 事件传递:忘记
ctx.fireChannelRead(msg)会导致事件截断,后面 handler 收不到消息 - 入站、出站流向相反,编解码器顺序很容易写反,是高频 bug 点
- channel.attr 适合存会话,注意 AttributeKey 全局唯一,注意内存泄漏
- RPC 框架中,Netty 只负责字节传输;序列化、报文协议、请求 ID 匹配、超时、重试、注册中心,都属于 RPC 上层能力
六、结语
Channel 是 Netty 对连接的抽象,Handler 是事件处理器,Pipeline 责任链是整个 Netty 事件流转的骨架。理解这套模型,不仅可以写 IM、网关,更是自研 RPC 框架的底层基础。后续可以基于这套架构继续扩展:注册中心、服务发现、负载均衡、熔断限流、压缩、多种序列化方案。