Mina2.0工作原理以及配置注意事项
- 格式:pdf
- 大小:247.92 KB
- 文档页数:23
MINA框架简介Apache MINA(Multipurpose Infrastructure for Network Applications) 是用于开发高性能和高可用性的网络应用程序的基础框架,它对Java中的socket和NIO进行了有效和清晰的封装,方便开发人员开发TCP/UDP 程序,从而抛开在使用原始的socket时需要考虑的各种繁杂而又烦人问题(线程、性能、会话等),把更多精力专著在应用中的业务逻辑的开发上。
Mina 综述Multipurpose Infrastructure for Network Applications。
一个基于非阻塞I/O的网络框架。
高可维护性,高可复用性:网络I/O编码,消息的编/解码,业务逻辑互相分离。
与JMX结合。
使用sfj4作为log支持UDP,支持客户端API。
由Netty2的作者Trustin Lee开始开发的。
相对容易进行单元测试客户端通信过程1.通过SocketConnector同服务器端建立连接2.链接建立之后I/O的读写交给了I/O Processor线程,I/O Processor是多线程的3.通过I/O Processor读取的数据经过IoFilterChain里所有配置的IoFilter,IoFilter进行消息的过滤,格式的转换,在这个层面可以制定一些自定义的协议4.最后IoFilter将数据交给Handler进行业务处理,完成了整个读取的过程5.写入过程也是类似,只是刚好倒过来,通过IoSession.write写出数据,然后Handler进行写入的业务处理,处理完成后交给IoFilterChain,进行消息过滤和协议的转换,最后通过I/O Processor将数据写出到socket通道IoFilterChain作为消息过滤链1.读取的时候是从低级协议到高级协议的过程,一般来说从byte字节逐渐转换成业务对象的过程2.写入的时候一般是从业务对象到字节byte的过程IoSession贯穿整个通信过程的始终整个过程可以用一个图来表现消息箭头都是有NioProcessor-N线程发起调用,默认情况下也在NioProcessor-N线程中执行MINA2网络应用架构基于Apache MINA 的网络应用有三个层次,分别是I/O 服务、I/O 过滤器和I/O 处理器:∙I/O 服务:I/O 服务用来执行实际的I/O 操作。
Mina源码阅读笔记,详情请关注:/ielts0909/blog写了很多关于Apache Mina的文章,为了方便大家阅读,我将关于mina的一些文章做点儿索引。
Mina官网资料-----------------------------------------------------------------Mina官网是学习mina最重要的地方:Apache MinaMina官网上有教你快速上手的quick start:《Quick Start》Mina官网提供了User Guide,这个手册包含了很多概念和示例:《User Guide》Mina的应用博客--------------------------------------------------------------Mina基于NIO开发中间有个重要的桥梁缓冲区,了解两者不同:《IoBuffer和ByteBuffer》Mina实现自定义协议的通信、对通信的一些思考:《Mina实现自定义协议通信》、《Tcp 传输下的思考》、《NIO开发的思考》Mina提供状态机来实现复杂的业务控制:《Mina状态机State Machine》Mina源码解读-----------------------------------------------------------------《Mina源码阅读笔记(一)-整体解读》《Mina源码阅读笔记(二)- IoBuffer的封装》《Mina源码阅读笔记(三)-Mina的连接IoAccpetor》《Mina源码阅读笔记(四)—Mina的连接IoConnector》《Mina源码阅读笔记(五)—Mina对连接的操作IoSession》《Mina源码阅读笔记(六)—Mina异步IO的实现IoFuture》《Mina源码阅读笔记(七)—Mina的拦截器FilterChain》《Mina源码阅读笔记(八)—Mina拦截器器的末端IoHandler》题外话--------------------------------------------------------------------------Mina的系列就写到这里了,后面如果有心得体会还会再更新上去,接下来我主要想写的是分布式消息系统的内容。
MINA2实用手册作者:李庆丰Email:scholers@MINA框架是对java的NIO包的一个封装,简化了NIO程序开发的难度,封装了很多底层的细节,然开发者把精力集中到业务逻辑上来,最近做了一个相关的项目,为了备忘对MINA做一个总结。
一、服务端初始化及参数配置MINA2初始化很简单。
基本的初始化参数如下://初始化Acceptor—可以不指定线程数量,MINA2里面默认是CPU数量+2 NioSocketAcceptor acceptor = new NioSocketAcceptor(5);java.util.concurrent.Executor threadPool =Executors.newFixedThreadPool(1500);//建立线程池//加入过滤器(Filter)到Acceptoracceptor.getFilterChain().addLast("exector", newExecutorFilter(threadPool));//编码解码器acceptor.getFilterChain().addLast("codec",new ProtocolCodecFilter(new WebDecoder(),newXmlEncoder()));//日志LoggingFilter filter = new LoggingFilter();filter.setExceptionCaughtLogLevel(LogLevel.DEBUG);filter.setMessageReceivedLogLevel(LogLevel.DEBUG);filter.setMessageSentLogLevel(LogLevel.DEBUG);filter.setSessionClosedLogLevel(LogLevel.DEBUG);filter.setSessionCreatedLogLevel(LogLevel.DEBUG);filter.setSessionIdleLogLevel(LogLevel.DEBUG);filter.setSessionOpenedLogLevel(LogLevel.DEBUG);acceptor.getFilterChain().addLast("logger", filter);acceptor.setReuseAddress(true);//设置的是主服务监听的端口可以重用acceptor.getSessionConfig().setReuseAddress(true);//设置每一个非主监听连接的端口可以重用MINA2中,当启动一个服务端的时候,要设定初始化缓冲区的长度,如果不设置这个值,系统默认为2048,当客户端发过来的消息超过设定值的时候,MINA2的机制是分段接受的,将字符是放入缓冲区中读取,所以在读取消息的时候,需要判断有多少次。
最近一直在看Mina的源码,用了Mina这么长时间,说实话,现在才开始对Mina有了一些深刻的理解,关于Mina的基本知识的介绍,这里就不多说了,网上已经有很多不错的文章都对Mina做了较深刻的剖析,现在就是想从Mina的最根本的地方来对Mina做一些深层次上的探讨。
还是先从Mina的入口程序来说,每当要启动一个Mina的程序(包括服务器和客户端)时候,这里只是对服务器重点做一些讲解,至于说Mina的客户端的应用,这里只是简单的涉及一点,不会对其做很深入的探讨。
但是Mina的服务器和客户端在很大的程度上都是一样,所以这里就“挂一漏万”的简单讲解一下。
在此之前我一直想找一种“串糖葫芦”的方式来讲解一下Mina,可是一直没有时间来看Mina的源码,真的是无从下手,虽然网上的很多关于Mina的一些文章,讲解的非常透彻了,但是可能对于初学者来说,显得有些深奥,在这里特别的提一下洞庭散人对Mina源码的透彻的分析,如果你对Mina已经有了一定的了解或者是正在学习Mina的源码,建议你去看看他的博客,里面有很多东西讲的是相当到位的。
在这里就不在多举例子了。
写这篇文档主要是想对刚接触Mina的人讲解一些Mina的基本知识,由浅入深,一步一步的学习Mina思想的精髓,我接触Mina的时间也比较长了,几乎天天在和它打交道,每当你发现一个新奇的用法的时候,你真的会被Mina所折服,我这里不是对Mina的吹捧,记得我曾经和同事开玩笑说,“等真正的懂得了Mina,你就知道什么叫Java了”,所以,我现在想急切的把现在所知道和了解的所有关于Mina的一些东西都想在这篇文章里面写出来,如果有写的不到位的地方还请各位同学多多指正,下面就开始对Mina做一个完整的介绍。
第一章Mina的几个类先说说Mina的几个类和接口(1) IoService(2) BaseIoService(3) BaseIoAcceptor(4) IoAcceptor(5) IoConnector这几个类和接口是整个服务器或客户端程序(IoConnector)的入口程序,其中就Mina 的整体上来说,IoService是所有IO通信的入口程序,下面的几个接口和类都是继承或者实现了IoService接口。
深入理解Apache Mina----配置Mina的线程模型在Mina的使用中,线程池的配置一个比较关键的环节,同时它也是Mina性能提高的一个有效的方法,在Mina的2.0以上版本中已经不再需要对Mina线程池的配置了,本系列文章都是基于当前的稳定版本Mina1.1.7版来进行讲述的,Mina的2.0以上版本现在还都是M(millestone,即里程碑)版的,在1.5版本上2.0M版为稳定版本,但是在1.5+以上则为非稳定版本,所以,为了更好的进行讨论和学习,还是基于Mina1.1.7版本进行讨论,如果使用Mina2.0进行开发要注意JDK的版本问题,当然如果有能力的话也可以自行修改和编译Mina的2.0版本,这里对此就不再多说,使用2.0版本的同学可以不用理会本文的内容。
上面的内容都是基于Apache Mina提供的文档讲述,如有需要,请自行查找相关资料,在此不再赘述。
下面开始对Mina的线程模型的配置、使用、及ExcutorFilter的基本原理进行简单的讲解。
(一)配置Mina的三种工作线程在Mina的NIO模式中有三种I/O工作线程(这三种线程模型只在NIO Socket中有效,在NIO数据包和虚拟管道中没有,也不需要配置):(1)Acceptor thread该线程的作用是接收客户端的连接,并将客户端的连接导入到I/O processor线程模型中。
所谓的I/O processor线程模型就是Mina的I/O processor thread。
Acceptor thread在调用了Acceptor.bind()方法后启动。
每个Acceptor只能创建一个Acceptor thread,该线程模型不能配置,它由Mina自身提供。
(2)Connector thread该线程模型是客户端的连接线程模型,它的作用和Acceptor thread类似,它将客户端与服务器的连接导入到I/O processor线程模型中。
客户端通信过程1.通过SocketConnector同服务器端建立连接2.链接建立之后I/O的读写交给了I/O Processor线程,I/O Processor是多线程的3.通过I/O Processor读取的数据经过IoFilterChain里所有配置的IoFilter,IoFilter进行消息的过滤,格式的转换,在这个层面可以制定一些自定义的协议4.最后IoFilter将数据交给Handler进行业务处理,完成了整个读取的过程5.写入过程也是类似,只是刚好倒过来,通过IoSession.write写出数据,然后Handler进行写入的业务处理,处理完成后交给IoFilterChain,进行消息过滤和协议的转换,最后通过I/O Processor将数据写出到socket通道IoFilterChain作为消息过滤链1.读取的时候是从低级协议到高级协议的过程,一般来说从byte字节逐渐转换成业务对象的过程2.写入的时候一般是从业务对象到字节byte的过程IoSession贯穿整个通信过程的始终整个过程可以用一个图来表现消息箭头都是有NioProcessor-N线程发起调用,默认情况下也在NioProcessor-N线程中执行类图/class-diagrams.html#ClassDiagrams-ProtocolDeco derclassdiagramConnector作为连接客户端,SocketConector用来和服务器端建立连接,连接成功,创建IoProcessor Thread(不能超过指定的processorCount),Thread由指定的线程池进行管理,IoProcessor 利用NIO框架对IO进行处理,同时创建IoSession。
连接的建立是通过Nio的SocketChannel进行。
NioSocketConnector connector = new NioSocketConnector(processorCount); ConnectFuture future = connector.connect(new InetSocketAddress(HOSTNAME, PORT));建立一个I/O通道Acceptor作为服务器端的连接接受者,SocketAcceptor用来监听端口,同客户端建立连接,连接建立之后的I/O操作全部交给IoProcessor进行处理IoAcceptor acceptor = new NioSocketAcceptor();acceptor.bind( new InetSocketAddress(PORT) );Protocol利用IoFilter,对消息进行解码和编码,如以下代码通过 MyProtocolEncoder 将java对象转成byte串,通过MyProtocalDecoder 将byte串恢复成java对象Java代码1.connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new MyProtocalFactory()));2.......3.public class MyProtocalFactory implements ProtocolCodecFactory{4. ProtocolEncoderAdapter encoder = new MyProtocolEncoder();5. ProtocolDecoder decoder = new MyProtocalDecoder() ;6. public ProtocolDecoder getDecoder(IoSession session) throws Exception {7. return decoder;8. }9. public ProtocolEncoder getEncoder(IoSession session) throws Exception {10. return encoder;11. }12.}13.......14.public class MyProtocalDecoder extends ProtocolDecoderAdapter{15.16. public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out)17. throws Exception {18.19. int id = in.getInt();20. int len = in.getInt();21. byte[] dst = new byte[len];22.23. in.get(dst);24.25. String name = new String(dst,"GBK");26.27. Item item = new Item();28. item.setId(id);29. item.setName(name);30. out.write(item);31. }32.}33.......34.public class MyProtocolEncoder extends ProtocolEncoderAdapter {35.36. public void encode(IoSession session, Object message,37. ProtocolEncoderOutput out) throws Exception {38. Item item = (Item)message;39. int byteLen = 8 + item.getName().getBytes("GBK").length ;40. IoBuffer buf = IoBuffer.allocate(byteLen);41. buf.putInt(item.getId());42. buf.putInt(item.getName().getBytes("GBK").length);43. buf.put(item.getName().getBytes("GBK"));44. buf.flip();45. out.write(buf);46.47. }48.}handler具体处理事件,事件包括:sessionCreated、sessionOpened、sessionClosed、sessionIdle、exceptionCaught、messageReceived、messageSent。
第一章MINA前述1.1线程模型MINA线程模型采用了Reactors in threads模型,即Main Reactor + Sub Reactors的模式。
由main reactor处理连接相关的任务:accept、connect等,当连接处理完毕并建立一个socket连接(称之为session)后,给每个session分配一个sub reactor,之后该session的所有IO、业务逻辑处理均交给了该sub reactor。
每个reactor均是一个线程,sub reactor中只靠内核调度,没有任何通信且互不打扰。
现在来讲讲我对线程模型演进的一些理解:Thread per Connection:在没有nio之前,这是传统的java网络编程方案所采用的线程模型。
即有一个主循环,socket.accept阻塞等待,当建立连接后,创建新的线程/从线程池中取一个,把该socket连接交由新线程全权处理。
这种方案优缺点都很明显,优点即实现简单,缺点则是方案的伸缩性受到线程数的限制。
Reactor in Single Thread:有了nio后,可以采用IO多路复用机制了。
我们抽取出一个单线程版的reactor模型,时序图见下文,该方案只有一个线程,所有的socket连接均注册在了该reactor上,由一个线程全权负责所有的任务。
它实现简单,且不受线程数的限制。
这种方案受限于使用场景,仅适合于IO密集的应用,不太适合CPU密集的应用,且适合于CPU资源紧张的应用上。
Reactor + Thread Pool:方案2由于受限于使用场景,但为了可以更充分的使用CPU资源,抽取出一个逻辑处理线程池。
reactor仅负责IO任务,线程池负责所有其它逻辑的处理。
虽然该方案可以充分利用CPU资源,但是这个方案多了进出thread pool的两次上下文切换。
Reactors in threads: 基于方案3缺点的考虑,将reactor分成两个部分。