为什么说AIO是被“误解”,虽然这个“误解”是用双引号标出来的,但是还是不得不承认它的发展现状并不好。AIO是Java7提供的新特性,而这个“新特性”到现在已经成为老酒,鲜有人尝。要知道Java7是在2011年7月发布的,市面上基于AIO的通信框架非常少,而且关于这项技术的介绍文章也普遍比较粗糙。从描述AIO的文章来看,似乎人们从学术层面上不太喜欢这项技术。作为AIO的学习者和受益者,我觉得有必要对网络上的一些“偏见”发表一下看法。如果有幸在认知上达成共识,后续的学习和交流会更加顺畅。通常偏差来自于对比,AIO与BIO、NIO的对比详情见表。误区一从上表对比可以看出AIO的性价比应该比NIO好,但实际情况是大多数人更喜欢NIO。准确的说,他们应该更喜欢NIO通信框架:Netty。这无可厚非,Netty确实是一个非常好的项目,但是很多人在Github上错误解读了Netty不支持AIO的理由,进一步遏制了AIO的发展。NotfasterthanNIO(epoll)onunixsystems(whichistrue)这句话的原意应该是:NIO和AIO在unix系统上都是使用epoll方式,本质是一样的。ButNotfasterthanNIO一定程度上会被误解为AIO没有NIO快。这里可以用假设的方法来证明这个观点是不成立的。假设:epoll的性能为x=100;因为通信框架需要解决并发调度和资源分配的问题,封装epoll后会有一定的性能损失,用y表示。最终的性能结果应该是r=x-y。证明:某NIO框架基于epoll封装的性能损失值:y=5,则其最终性能为:x-y=95。如果有一个AIO框架可以将性能损失值控制在:y=(0,5),那么最终的性能会比NIO框架更高。如果y>5,性能低于NIO框架。结论:根据底层模型是kqueue、epoll、select还是IOCP来比较NIO和AIO的性能是不准确的。决定在于框架实现可以利用多少基本功能。不然也是用的NIO技术,为什么不同的框架还是有高低之分。误区二Linux系统的AIO还不成熟。如果是这个原因,不妨看看:http://lse.sourceforge.net/io/aio.html,核心一句话:SupportforkernelAIOhasbeenincludedinthe2.6Linuxkernel。请注意,Linux内核从2.6版本开始支持AIO模式。这是一个很奇怪的现象。好像AIO一次不支持就永远不支持了,出现过的bug会一直存在。就像JAVANIO的空训练bug,现在已经发展到Java13了,还有人坚信这个bug一直存在。坦白说,我没有验证过JavaAIO在Linux环境下是不是真正的AIO,也没有复现过NIO空训练的bug。但如果你因为某种原因放弃了不断的学习,那么你对事物的认知和认识就只能停留在过去。所以“Linux系统的AIO还不成熟”不会成为我放弃AIO的理由。误解3需要为每个连接预先分配读缓存。这确实是客观情况。AIO的使用方式是调用读写接口注册ByteBuffer对象。当事件完成时,以回调的形式触发CompletionHandler,所以必须提前分配好缓冲空间。但是有一个细节可能被大家忽略了。即使使用了NIO,当遇到half/sticky包的时候,还是需要一个缓存对象来暂存这些不完整的数据。特别是在高并发场景下,半包/粘包现象容易加剧。这时候NIO需要分配的缓存并不比AIO少多少。即使假设理想情况下没有半包/粘包问题,AIO通信的预分配形式可以消耗多少额外内存。为每个连接分配1024字节的读缓存,在10000个并发连接的情况下消耗内存不到10MB。一个Java应用服务器在真实场景下需要同时支持多少并发,10000到50000?100000?。目前已知的通信框架通常都带有内存池,在此前提下,AIO只是提前利用内存池中的资源。同样的内存池配置,同样的并发压力下,如果AIO暴露内存问题,我们会再次选择AIO和NIO。综上所述,本文无意反对NIO和AIO。两种技术都非常有吸引力,喜欢技术的朋友可以在这方面钻研很久。个人推荐纯粹是为了学习和优先考虑NIO,因为它更难,更有挑战性,会面临和需要解决的问题也更多。如果在学习过程中遇到困惑,可以再去翻AIO的源码,里面有很多值得借鉴的设计。
