netty手动关闭资源的探究

最近突然得知自己的基础差,与精英的标准相距甚远。的确,平时的工作中,有太多机会允许我运用“基础”以外的东西去完成。让我对“基础”没有很重视,所以决定了马上亡羊补牢。就先从自己最看重的异步编程开刀吧,翻阅起了《Netty权威指南》。大部分内容与看之前的道听途说、自行猜想没差多少。突如其来的如下代码,让我产生了警觉:


netty手动关闭资源的探究_第1张图片
1.png

代码中,由于手动打开了本地文件,所以在flush操作后,手动关闭了文件。但netty作为一个异步框架,总觉得不太可能需要写出这种同步样式的代码。
我的疑问有两点:1、flush方法返回时,可以确保数据write完毕?2、netty没有提供用于编写收尾逻辑的回调接口?
弄清疑问一:
尝试在互联网上搜索,发现并没有关于这点的描述,所以干脆自己下载了源码自己看。
ChannelHandlerContext接口,对flush的描述:

netty手动关闭资源的探究_第2张图片
2.png

AbstractChannelHandlerContext抽象类中的具体实现:

netty手动关闭资源的探究_第3张图片
3.png

意思应该是将pipeline中的ChannelOutboundHandler依次调一遍,参照findContextOutbound的实现:

netty手动关闭资源的探究_第4张图片
4.png

可以得知,ChannelOutboundHandler是从后向前依次调用的。最后一个调用的为默认的HeadContext:

netty手动关闭资源的探究_第5张图片
5.png

HeadContext的flush,调用的是DefaultChannelPipeline.channel().unsafe().flush():

netty手动关闭资源的探究_第6张图片
6.png

netty手动关闭资源的探究_第7张图片
7.png

以书中的示例代码使用的NioServerSocketChannel为例,当接收到客户端请求时,创建了NioSocketChannel:

netty手动关闭资源的探究_第8张图片
8.png

netty手动关闭资源的探究_第9张图片
8+.png

NioSocketChannel中unsafe().flush()的调用链为:
unsafe()[继承自:AbstractNioByteChannel、AbstractNioChannel] >>> AbstractChannel.unsafe()
AbstractChannel.unsafe()返回的结果来自:
NioSocketChannel.newUnsafe()

9.png

NioSocketChannelUnsafe的flush方法[继承自:NioByteUnsafe、AbstractNioUnsafe、AbstractUnsafe]:

netty手动关闭资源的探究_第10张图片
10.png

调用的flush0方法:
flush0()[继承自:NioByteUnsafe、AbstractNioUnsafe] >>> AbstractUnsafe.flush0()

netty手动关闭资源的探究_第11张图片
11.png

调用的NioSocketChannel.doWrite方法:

netty手动关闭资源的探究_第12张图片
12.png

针对示例中的FileRegion,调用了父类doWrite方法:

netty手动关闭资源的探究_第13张图片
13.png

netty手动关闭资源的探究_第14张图片
14.png

其中确实存在针对write未完毕的处理,调用用incompleteWrite(true) >>> setOpWrite():

netty手动关闭资源的探究_第15张图片
15.png

将SelectionKey.OP_WRITE注册到selector中,并在写操作完成后,由selector再触发一次flush(NioEventLoop代码):

16.png

总上,flush方法返回时,不保证数据write完毕。
弄清疑问二:
DefaultFileRegion的API说明:
Default FileRegion implementation which transfer data from a FileChannel or File. Be aware that the FileChannel will be automatically closed once AbstractReferenceCounted.refCnt() returns 0.
DefaultFileRegion类实现了FileRegion接口,继承了AbstractReferenceCounted类。创建DefaultFileRegion实例时可以传入一个FileChannel实现或一个File实例,而且在这里使用FileChannel不需要手动close。
上文的AbstractNioByteChannel.doWrite方法中,完毕时调用了ChannelOutboundBuffer.remove():

netty手动关闭资源的探究_第16张图片
17.png

这正是用来释放ReferenceCounted对象的逻辑。
DefaultFileRegion中也存在关闭FileChannel的逻辑:

netty手动关闭资源的探究_第17张图片
18.png

但RandomAccessFile方法中,不只有关闭FileChannel的逻辑:

netty手动关闭资源的探究_第18张图片
19.png

因此RandomAccessFile.close()方法还是需要手动调用的。
总终方案:
编写DefaultFileRegion子类,重写deallocate方法,额外关闭RandomAccessFile对象。(代码略)

探究感想:
为了论证自己的猜测,确实花费了不少精力。最大的困难在于本次的论点,没有被广大程序员关注,而且错误的代码遍布各种书籍、网站,让人直觉上认为那些就是对的。这正让我联想到之前美团的招聘细则中不要“信中医的”。中医这里代表的是广泛留传下来的传统中医理论知识,不单只运用了现代科学医药手段验证过的中医。中医与软件理论,都十分庞大、且充满糟粕。对待中医和软件知识,都应该持有一种警觉态度,那些被记载、流传下来的手段、方案,真的是正确的吗?适用在你的场景中依然有效?的确,实际运用中会有众多因素,制约着你无法透彻的去验证。但你的知识获取方法,只能是听信他人吗。1753年英国海军军医伦达,经试验得出结论:柠檬汁可用来治疗和预防坏血病。这种的结论,对于航海而言就足够了,不需要知道其根本是维生素C的作用。中医知识通过大量随机双盲对照试验来验证。软件知识,归根到底就是代码,真有什么不明白的,看一看、运行一下。你能了解到什么程度,首先由你想了解到什么程度决定。

你可能感兴趣的:(netty手动关闭资源的探究)