MINA使用心得及相关要点,有一个bug的解决方案,不知道其它朋友是否遇到过(一)
我是个懒人,懒得出其的懒人,很多年来一直只索取,不回报的,因为太懒了,所以虽说注册时间比较早,但文章可以用一个手掌数过来。。。。。这次被领导逼的,写了个短文发个企业内部通讯上,所以就帖上来了,我还真是懒啊。。。格式都没改直接帖了。。。。。当然这其中 有些内容是参考了一些网上的文章,介绍mina的部分类似的语句还是有一些滴,但主要还是为了说明问题,重点在使用心得上面,所以介绍mina如何使用的东西不是特别多,这类文章网上到处都是,相对来说比较适合对MINA有一点了解的童鞋阅读。
大并发量socket通信的解决方案
笔者之前的工作主要是做java的web端开发,后因工作原因参与了一个国家级的大项目,主要负责其中底层通讯的前置机模块。几经波折,将该系统完成后,结果在第一轮的测试中就惨败退回。其根本原因就在于原设计文档的要求单“通信机”与“终端”(注一)之间的并发量要达到2W以上的连接通信,而实际运行并发量只能达到2600个相差了近十倍左右。经过代码调优、扩展JVM内存等等手段,但因基础数据相差过大,所取得的优化效果十分有限。后考虑在根本着手,只有更改整个系统的通信接口,才有可能达到设计文档上的要求。某天在某个技术QQ群里一次讨论中,有网友向我推荐了一个框架,这就是本文要介绍的主角-MINA。
?

Apache MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络应用程序提供了非常便利的框架。当前发行的 MINA 版本支持基于 JavaNIO 技术的 TCP/UDP 应用程序开发、串口通讯程序(只在最新的预览版中提供),MINA 所支持的功能也在进一步的扩展中
使用案例:目前正在使用 MINA 的软件包括有:ApacheDirectory Project、AMQP(Advanced Message Queuing Protocol)、RED5 Server(Macromedia Flash Media RTMP)、ObjectRADIUS、Openfire 等等。
Apache直属MINA的子项目:FTPServer,AsyncWeb,SSHD
其实在有人推荐了MINA之后,本人就上网google了一把,搜索一番下来之后才发现自己的眼界过于狭隘了,原来有相同功能的开源框架还真不少,看来以后得继续多泡论坛和QQ了(有正当理由上班泡坛子,聊QQ了,偷笑一个^_^。。。。。。)
同类框架:对于这些框架的基本使用和基础架构,本人都经过了一番研究,发现这些框架要么重者过重,要么轻者过轻。鉴于项目的规模及时间的紧迫,再三比较之下,本人还是选择了学习曲线低,性能优异的MINA. 至于这些优点是如何体现出来的,本文以下内容将继续为您解读。
MINA快速入门闲话少说,借用大师级的写书经验,咱们先来一个hello world。当然在这前我们还是得先做一些准备的。
预备知识:JAVA NIO
多线程
Socket
以上知识对本文的阅读理解有一定帮助,但并非一定必需。
?
资源下载:log4j:因为其中缺少log4j的包,所以做试验的朋友还需要去下一个log4j的包。
开发工具:eclipse
Jdk:1.6x
鉴于篇幅关系,类没有写全,更具体的内容,请大家参考mina压缩包里自带的demo。但实际上服务端需要手写的部分确实只有以上二块内容,服务端就算是写好了,由次可以看出mina的入门之快。现在我们来测试。打开cmd(这个对于同仁们来说,就不用详述了吧。。。- -!!),输入telnet 127.0.0.1 9998(这里的9998要与上面代码绑定的端口一致)随便输入一些字符,敲下回车后就会在控制台显示您所输入的信息了。
做得这一步的童鞋可以说对MINA的使用可以说已经初步入门了,是不是觉得太简单了?这也太假了吧,这就算入门了?呵呵。。。个人认为这就是MINA的强悍之一,简单的几行代码就搞定了一个服务端,要入门简直是太简单了。但任何事物都有其二面性,真要把一个东西学好,用好,还要不出错,我们需要了解的东西就太多太多了。下面我们就从MINA框架的底层说起,因为在实际应用当中,要解决一些碰到的难点及问题,对框架的整个运作体系必须要有个全面深入的了解。只有这样才能在碰到问题时,有目的,有针对性的去找到症结所在。
MINA深度了解Mina的应用层?

?
???????????????????????????????????? 图1
?
一个设计成熟的开源框架,总是会仅可能的减少侵入性,并在整个项目中找到合适的位置,而不应对整个项目的构架设计产生过多的影响,图1就是MINA的应用层示意图。从图中和上节的DEMO中我们可以看到,MINA很好的把业务代码和底层的通信隔离了开来,我们要做的仅仅是建立好监听,然后写上我们需要实现的业务逻辑就OK了。
?
MINA的内部流程?

???????????????????????????????????? 图2
通常在JAVA NIO 编码中,我们都是使用一个Selector,也就是不区分IoService与IoProcessor 两个功能接口。另外,IoProcessor也是MINA框架的核心组件之一.在MINA框架启动时,会用一个线程池来专门生成线程,来负责调用注册在IoService 上的过滤器,并在过滤器链之后调用IoHandler。在默认情况IoProcessor 会用N+1个线程来轮流询问监视的端口是否有数据传送,其中n为cpu的内核个数。按一般的多线程设计概念来说,IoProcessor的线程数是越多越好,但实际上并非如此,因为大家都知道,IO的操作是非常占用资源的,所以项目中的IoProcessor的线程数应该根据实际需要来定,而这个数字可以在生成IoAcceptor对象时进行设定。Eg IoAcceptor acceptor = newNioSocketAcceptor(N);
?
说了这么多,以上内容也只能让大家对MINA有个基础的了解,对于MINA框架优势的认识可能还不是很多,那么下面的内容,就此展开对比讨论,以便大家对MINA的适用场景及优点有个更全面的了解。
选择MINA的理由传统socket编程在传统I/O中,最简单实现高并发服务器的编程方式就是对每一个客户开启一个线程。但是这种方式有如下几个弊端:
客户端上限很大的情况下不能及时响应
服务器硬件资源受限,性能也会急剧下降
受制于操作系统的限制
但这种设计方式优点还是有的:
编码简单,实现容易
一定数量的连接性能比较好。
笔者的项目开发中,最开始就是采用的这种方式,写起来方便,控制起来也方便,但遗憾的是JVM最多只能开到2K多的线程,就会报- can not create new thread的错误。
?
(实现结构图见下:)

图3(一对一的结构图。)
改进的Socket编程为了解决每一个线程一个连接的模型,笔者最开始想到用多个线程处理N个用户,这样既可以保证处理多个用户的同时,线程开销降到系统的临界点。
这样的方式与前一个模型优势在于同样的多线程,但线程数固定,充分运用系统的优势性能,又不存在多余的开销。但是缺点也是显而易见的:
轮询的消耗不可避免。
一但产生io阻塞,其阻塞的时间纯属浪费。
客户数量固定的时候没有前一模型响应快
编码更加复杂。

图4(一对多)
使用MINA框架的编程为了解决上述的矛盾,最终的解决方案只能是异步的NIO,而随着笔者对JAVA? NIO的研究发现,要实现异步的NIO,并应用到实际项目中,必须对NIO有着比较深刻的了解和把握,笔者曾尝试着利用JAVA 原生 NIO接口写了一个DEMO(具体的使用方法,感兴趣的童鞋可以GOOGLE一把,你会发现用原生NIO写程序与使用MINA写程序对比起来是多么的痛苦。。。。 -_-!!),但由于笔者在这方面的底子过薄,试验结果不如人意,但要对NIO进行更为深入的学习,时间上面也不允许。直到MINA框架的映入眼帘,以上难题不再是问题。。。。以下是利用MINA的实现方式的一个简图。

?
???????????????????????????????????? 图5
?
其中IoService接口会专门起一个线程来轮询是否有新的连接产生,一旦有连接产生则通知IoProcessor,而IoProcessor则起n+1个线程来检查连接是否有数据在上面读写(注二)。一旦有连接产生,并有数据读写,则通知decode或ENCODE,进行报文的解码或编码,将处理后的报文再交给业务类进行业务处理。其中IoProcessor是处理请求的分配,包括选择Selector,超时验证,状态记录等。总之这个类和IoService一起配合工作,封装了NIO底层的实现以及MINA框架内部的功能的支持.由于过于复杂,篇幅所限所以不作详细讲解.
结合实例,并根据以上的图文讲解,我们可以很轻易的总结出利用MINA编程的几个大致步骤:
创建一个实现了IoService接口的类
设置一个实现了IoFilter接口的过滤器(如果有需要的情况下)
设置一个IoHandler 接口实现的处理类,用于处理事件(必须)
对IoService绑定一个端口开始工作
关于MINA的大致运行流程及使用步骤,我们就暂时分析到这,具体更细节的关于一些核心类的使用方法及自定义编码器的方法,大家可以直接参考mina中所带的几个案例,写得非常详细,足够解决大家在项目中碰到的大部分问题,接下来要与大家交流的是使用MINA时非常有可能遇到的一些扩展知识。
?
注二:这一点请特别注意,因IoProcessor也是相当于轮询机制,这导致在报文过长时,或其它原因导致报文不能一次传输完毕的情况下,必须保存同一连接(在MINA中是以IoSession类生成的对象)下的上一次状态,这样才能截取到一个完成的报文,而这也是Decode(编码器)需要做的核心工作,新手往往就在这上面要跌跟斗。
?
扩展知识这部分的内容要说起来跟MINA的使用关联不大,但实际情况是用上了MINA框架的项目基本上多多少少都会涉及到这一块,那就是多线程的编程。多线程的编程历来是JAVA编程中的重难点,很多新手碰到此类编程问题,往往都找不出原因所在,甚至一些有多年编程经验的程序员也会在这上面偶而犯下错,在这里笔者也没有能力通过很短的篇来解说多线程,那么就向大家介绍几个类及一些小常识吧,希望能给大家带来帮助。
iosession.close(false);//等socket发送完毕再关闭socket连接
并不是MINA官方说的,等socket发送完毕再关闭socket连接,而且会出现发送中关闭socket.
iosession.close(true);为立即关闭socket..晕晕的 3 楼 dennis_zane 2011-06-29 用带外数据来测试连接保活,我还是第一次见到,一般都是定义个应用层的心跳协议来做,没必要用带外数据搞。