版本定位:netty4.1.x
前言:在netty的api使用中,一般我们自定义处理器类的时候,会继承ChannelInboundHandlerAdapter或者SimpleChannelInboundHandler。而SimpleChannelInboundHandler是ChannelInboundHandlerAdapter的抽象子类,其中必须重写channelRead0抽象方法。看其方法源码:
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
boolean release = true;
try {
if (this.acceptInboundMessage(msg)) {
this.channelRead0(ctx, msg);
} else {
release = false;
ctx.fireChannelRead(msg);
}
} finally {
if (this.autoRelease && release) {
ReferenceCountUtil.release(msg); // 自动释放接收的信息占用的资源
}
}
}
channelRead0是在重写了父类的channelRead方法中调用的。
问题根源:
因为我们知道,当接收到数据的时候,会自动调用channelRead方法,那么这里重写的方法中明显最终会释放消息所占用的资源。而我们在重写channelRead0()方法的时候,有时场景需要处理接收的信息并发回给对端。比如:
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {
// 接收到的数据后,处理原信息并返回给对端
channelHandlerContext.channel().writeAndFlush(LocalDateTime.now() + "-" + msg);
TimeUnit.MILLISECONDS.sleep(500);
}
而writeAndFlush()源码中的write()方法是异步执行的,若其正在处理 msg,而此时 SimpleChannelInboundHandler 中的 channelRead()方法执行完毕了,则会将 msg 给释放了。此时就会报错。所以有丢失资源的可能。那么要解决这里的问题,注意使用Api的场景即可。
解决方案:
- 若对方没有向自己发送数据,则自定义处理器建议继承自ChannelInboundHandlerAdapter。因为若继承自 SimpleChannelInboundHandler 需要重写channelRead0()方法。而重写该方法的目的是对来自于对方的数据进行处理。因为对方根本就没有发送数据,所以也就没有必要重写 channelRead0()方法。
- 若对方向自己发送了数据,而自己又需要将该数据再发送给对方,则自定义处理器建议继承自 ChannelInboundHandlerAdapter。因为 writeAndFlush()源码中的write()方法的执行是异步的,且SimpleChannelInboundHandler 中的 channelRead()方法会自动释放掉来自于对方的 msg。而使用ChannelInboundHandlerAdapter的channeRead()时,可以创建新的
ByteBuf
对象来存储响应数据,而不必担心消息的内存资源被提前释放。但是注意释放ByteBuf的内存资源,以免发生内存泄漏。代码如下:
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 强制转换为ByteBuf
ByteBuf byteBuf = (ByteBuf) msg;
try {
// 处理接收到的消息,例如读取数据、解析等操作
// ...
// 释放ByteBuf的内存资源
byteBuf.release();
} catch (Exception e) {
// 异常处理
} finally {
// 一般在finally块中确保资源被释放
// 如果发生异常,也需要保证资源被释放
// ...
}
}
最后还是附上netty官网,知识源头~Netty: Home