MINA使用心得及相关要点,有一个bug的解决方案,不知道其它朋友是否遇到过(二)
?
IoBuffer接口IoBuffer是MINA中的独有接口,主要继承实现的是java NIO 中的ByteBuffer,所以从使用方法上来看二者区别不大,唯一比较大的区别就是,IoBuffer支持可变长的数据填充,对于这个类有三个关键属性,分别是
从本质上讲这三个方法都是对之前所介绍的三个属性进行操作,所以这里就会有一个误区,很多人从字面上解读clear()这个方法,以为这个方法就是将数据擦除。而实际上clear方法并不有擦除数据,仅仅是做了二件事,一是将limit=capacity,二是position=0,这使得数据好像被清除,以便接收下一次的读写。但请大家千万注意,在写自定义解(编)码器时一定不要随便使用这些方法,请确保在对这些方法有了足够的认识后(最好的方法就是解读源码),才去使用它。特别是直接使用clear()时,一旦使用不当,在截取数据非常容易发生死循环的情况,因为前面已经说过,clear并不是清除数据,而只是改变属性值,也就是说原数据依旧保留,这会导致某些时候会不停的重置这三个属性,去读同一段索引的数据,从而导致死循环。网上好多demo都是直接使用的clear(),其实都是存在一定隐患的,希望能看到这里的朋友能引起警惕,个人认为最好的清除方式就是人为的控制Iobuffer的三个属性值。以下是参考代码:
?? ?int oldLimit = in.limit();
??? .......开始..........
??? 处理数据
??? .......结束..........
??? in.position(pos);
这个方法的作用,只要是用过socket编程的童鞋都知道它的作用。没错,它就是通过发送一个紧急字符到服务端(对socket来说实际上并不存在严格意义上的客户端或服务端,谁主动谁就是客户端),用来测试连接是否保持的一个方法。使用这个方法有二个限制。一是必须对方的OS支持该方法,二是要求服务端的SO_OOBINLINE为false.否则服务端将会把这个紧急字符认为是正常报文一并接收,而不是抛弃。反之当SO_OOBINLINE为true时,这个紧急字符仅仅用来确认通讯是否正常之用,服务端接收后会立刻抛弃不予处理的。默认情况下SO_OOBINLINE的值就是false,所以一般情况下,客户端直接用这个方法就可以测试连接是否保持了。
按理来说这个默认设置是个好事,但在笔者的项目开发中曾经因为这个sendUrgentData()被困扰了近一周的时间。事情的起因要从性能测试开始说起。
测试人员在测试过程中发现当前置机启动后有连接产生时就会让CPU占用率高居不下,开始笔者不是很在意,认为这个时间里有IO操作,CPU高居不下很正常,后来进一步测试发生在没有数据发送的情况下CPU也会占到近50%左右,这个现象就很不正常了,于是折腾开始。
先是确认前置机的哪个部分会占用cpu,很快将目标锁定到了调度机,接下来对调度机进行代码排查,没有发生任何问题,头大了,再次进入QQ群讨论该问题,有人向我推荐了JRMC(这也是我要向大家强烈推荐该工具的原因,网络的力量是强大的!!!嘿嘿。。。),通过这个监视工具,本人很快就再次缩小目标,将目标定位到一个叫Ioprocesse-1的线程上面,从名字及本文之前介绍的内容来看,很明显这个MINA框架内部线程导致的,随后就到网上查找是否有同类的现象,很遗憾本人可能是遇到一个前所未有的问题了,网络上提到使用MINA导致CPU占用率过高的内容几乎没有,无奈之下本人试着换JDK版本、MINA版本、甚至改写MINA源代码,一番折腾下来,结果是统统做了无用功。因为这个问题暂时不会影响测试和使用再加上时间过紧,后来就暂时将这个问题搁置了起来。某天,在开发的过程中笔者忽然想到:前置机的三个部分都是独立程序,通信机的接口也都是用mina改写的,为什么终端与通讯机没有这种现象发生呢?一番推理之下,本人反而将目标锁定到了占用率正常的通机机上面了,通过反复的排查,最终将问题锁定到了方法级,那就是sendUrgentData这个罪魁祸首。
本人试验发现,只要没有调用sendUrgentData方法所有一切都很正常,但通信机一旦调用sendUrgentData方法用来测试与调度机是否保持通信时,就会让调度机的CPU占用率瞬间飙升。但一个通信程序测试连接的方法是必不可少,而且暂时没有更好的能代替sendUrgentData的方法,所以就想着去改造sendUrgentData的源代码,结果一番跟踪下来才傻了眼,原来sendUrgentData方法的底层实现是native类型(注三)根本就无从改起,最终本人将注意打到了调度机的解码器上面。代码比较多笔者就不帖了,只写上具体的解决步骤,有实际需求的,欢迎讨论。
至此问题解决,但从解决方法来看,这个方法并不具有代表性,当碰到以下情况是并不一定适用:
当然这个问题的终结解决方案并不在本文的讨论范围之类,这个很明显就是mina的一个BUG,不知道2.04的版本有没有解决这个问题,但从官网所贴的问题列表来看,这个BUG应该是依旧存在的,奈何笔者E文比较烂,看看资料还可以,要我动手写,并将以上内容用E文表达出来,确实是没有那个勇气的,希望能看到这一段的童鞋能代劳一、二,督促官方早日改好这个BUG,咱也算是为开源软件尽了一份力不是? ^_^
有的童鞋看到这里可能会说笔者是不是太啰嗦了,几句话可以解决的事情讲这么多,这里我想强调一下的是,本文的核心内容主要讲的还是使用心得,而不是使用方法,在这里之所以把解决过程讲得这么详细,还是希望达成二点目的。
当然了,群众的力量是无穷的,大家嫌我太罗嗦的话,下面的内容我就简单一点吧。
?
这里主要推荐二个集合类,分别是ConcurrentLinkedQueue,ConcurrentHashMap这二个类的使用基本上不需要你考虑是不是多线程的编程,也不需要用锁,可以大幅提高并发量过大时的对像存取,至于实现机制劳驾大家自己去GOOGLE一把吧。
结尾语文章到这里基本上算是结束了,而项目最终的并发量从2680提升至3.5W,客户要求的是2W的并发量,所以后面更高的并发没有继续再测,至本文截稿为止,最新的消息是项目的一期工作已经顺利通过了国家级机构的评测。但实质上本文关于MINA框架还有很多东西没有涉及到,比如说与Spring的结合,对Jmx的支持,自定义协议详细举例等等,但我想这点小困难应该不会妨碍大家对mina的学习热情吧!感兴趣的童鞋不妨把它当成一个课后作业来做做吧!!? ^_^
//给IoHander增加独立的操作线程,与IO的线程独立开来filterChain.addLast("IoHandlerThread", new ExecutorFilter(Executors.newScheduledThreadPool(5/9)));