现状大概是这样: ChunkedFile cf= new ChunkedFile(file); channel.writeAndFlush(cf); 做一个ChunkedFile,然后直接写channel。
问题: 上传文件时,恰好发现连接断开了。 file的引用没有释放,导致后续再次上传后,无法删除文件。 ChunkedFile里实际就是包装了一个RandomAccessFile对象。 ---------------------------------------------------- 在ChannelPipeline里是这样: p.addLast(new ChunkedWriteHandler());//发送大文件
看了ChunkedWriteHandler的代码,各种处理很仔细,成功失败都有close动作。 有点困惑。 ---------------------------------------------------- 在业务Handler里,channelInactive()方法发现channel inactive后,直接重新连接了。 所以问题在于,连接断开时,没有释放file的引用。
然后突然想到,channelInactive()方法会触发哪个Hander来处理呢?
问了机器人,答案是:链式处理,从尾巴到Head。 ----------------------------------------------------
当 Channel 变为非活动状态时,channelInactive() 会从 pipeline 的尾端向头端 传播: ChannelInboundHandler1 → ChannelInboundHandler2 → ... → TailContext 实际上事件是从 TailContext 开始,向 HeadContext 方向传播(即从尾到头)。
public class ChannelInactiveExample { public static void main(String[] args) { ChannelPipeline pipeline = ...; // 添加 handler(按顺序) pipeline.addLast("handler1", new MyHandler("handler1")); pipeline.addLast("handler2", new MyHandler("handler2")); pipeline.addLast("handler3", new MyHandler("handler3")); // 触发 channelInactive 时,调用顺序是: // handler3 → handler2 → handler1 } static class MyHandler extends ChannelInboundHandlerAdapter { private String name; public MyHandler(String name) { this.name = name; } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.out.println(name + ": channelInactive"); super.channelInactive(ctx); // 重要:继续传播 } } }
必须调用 super.channelInactive(ctx)!!!! 如果不调用父类方法,事件传播会中断。 所以,根本原因是业务Handler的channelInactive()方法没有调用super.channelInactive(ctx)!!!! 导致ChunkedWriteHandler的channelInactive()方法没有执行,没有释放file引用。
ChunkedWriteHandler.java @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { doFlush(ctx); ctx.fireChannelInactive(); } private void doFlush(final ChannelHandlerContext ctx) { final Channel channel = ctx.channel(); if (!channel.isActive()) { discard(null); return; } 在discard()里会closeInput(in);
在父类: ChannelInboundHandlerAdapter.java中有: public void channelInactive(ChannelHandlerContext ctx) throws Exception { ctx.fireChannelInactive(); } 所以,业务Handler里应该调用super.channelInactive()方法!!!!!除非真的不需要!!!
|