美文网首页程序员
netty手动关闭资源的探究

netty手动关闭资源的探究

作者: 2016晓 | 来源:发表于2017-07-13 14:28 被阅读0次

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


    1.png

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

    2.png

    AbstractChannelHandlerContext抽象类中的具体实现:

    3.png

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

    4.png

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

    5.png

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

    6.png
    7.png

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

    8.png
    8+.png

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

    9.png

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

    10.png

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

    11.png

    调用的NioSocketChannel.doWrite方法:

    12.png

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

    13.png
    14.png

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

    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():

    17.png

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

    18.png

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

    19.png

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

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

    相关文章

      网友评论

        本文标题:netty手动关闭资源的探究

        本文链接:https://www.haomeiwen.com/subject/qdvwhxtx.html