这博文的系列主要是为了更好的了解一个完整的nio框架的编程细节以及演进过程,我选了同父(Trustin Lee)的两个框架netty与mina做对比。版本涉 及了netty3.x、netty4.x、mina1.x、mina2.x、mina3.x。这里并没有写netty5.x的细节,看了 netty5的修改文档 ,似乎有一些比较有意思的改动,准备单独写一篇netty4.x与netty5.x的不同。 netty从twitter发布的这篇《 Netty 4 at Twitter: Reduced GC Overhead 》文章让国内Java界为之一振,也小火了一把,同时netty的社区发展也不错,版本迭代非常快,半年不关注大、小版本就发了好几轮了。但是mina就有点淡了,github上面它最后大多数代码最后的修改日期均在2013年,不过我从个人情感上还是挺喜欢mina3的代码,没有太多的用不上的功能(支持各种协议啥的),跑自带的benchmark性能也比netty4好一些。但是如果是生产用的话,就偏向netty多一些了,毕竟社区活跃,版本迭代也快。 1. mina、netty的线程模型mina与netty都是Trustin Lee的作品,所以在很多方面都十分相似,他们线程模型也是基本一致,采用了Reactors in threads模型,即Main Reactor + Sub Reactors的模式。由main reactor处理连接相关的任务:accept、connect等,当连接处理完毕并建立一个socket连接(称之为session)后,给每个 session分配一个sub reactor,之后该session的所有IO、业务逻辑处理均交给了该sub reactor。每个reactor均是一个线程,sub reactor中只靠内核调度,没有任何通信且互不打扰。 在前面的博文:[ Netty 4.x学习笔记 – 线程模型 ],对netty的线程模型有一定的介绍。现在来讲讲我对线程模型演进的一些理解:
以上图片及总结参考:《Linux多线程服务端编程》 2. mina、netty的任务调度粒度mina、netty在线程模型上并没有太大的差异性,主要的差异还是在任务调度的粒度的不同。任务从逻辑上我给它分为成三种类型:连接相关的任务 (bind、connect等)、写任务(write、flush)、调度任务(延迟、定时等),读任务则由selector加循环时间控制了。 mina、netty任务调度的趋势是逐渐变小,从session级别的调度 -> 类型级别任务的调度 -> 任务的调度。 代码
分析mina1、2的任务调度粒度为session。mina会将有IO任务的的session写入队列中,当循环执行任务时,则会轮询所有的session,并依次把session中的所有任务取出来运行。这样粗粒度的调度是不公平调度,会导致某些请求的延迟很高。 mina3的模型改动比较大,代码相对就比较难看了,我仅是随便扫了一下,它仅提炼出了writeQueue。 而netty3的调度粒度则是按照IO操作,分成了registerTaskQueue、writeTaskQueue、eventQueue三个 队列,当有IO任务时,依次processRegisterTaskQueue、processEventQueue、 processWriteTaskQueue、processSelectedKeys(selector.selectedKeys)。 netty4可能觉得netty3的粒度还是比较粗,将队列细分成了taskQueue和delayedTaskQueue,所有的任务均放在 taskQueue中,delayedTaskQueue则是定时调度任务,且netty4可以灵活配置task与selectedKey处理的时间比 例。 BTW: netty3.6.0之后,所有的队列均合并成了一个taskQueue 有意思的是,netty4会优先处理selectedKeys,然后再处理任务,netty3则相反。mina1、2则是先处理新建的session,再处理selectedKeys,再处理任务。 难道selectedKeys处理顺序有讲究么? |
|
来自: 博雅书屋lhs > 《JAVA高性能编程》