MINA 网络库的剖析
一、MINA 概述
Mina是Apache社区维护的一个开源的高性能IO框架,它的底层还是利用了jdk提供了nio功能,mina只是对nio进行封装,包括MINA用的线程池都是jdk直接提供的。
1.1 MINA 的工作流程
Mina的主端由accept、prossser,session 组成其中accept主要负责事件的监听,若有新连接则建立一个新的session;cessor则负责处理session对应的发送数据和接收数据并调用上层处理;session则缓存当前连接数据。
具体过程:
1.Accept
Mina采用懒启动的模式,即最少启动线程,在MINA server启动的时候,只有一个线程-accept,并且accept线程只有一个,在指定的端口进行监听(可以同时监听多个端口,mina可以绑定多端口)
在服务器端,bind一个端口后,会创建一个Acceptor线程来负责监听工作。这个线程的工作只有一个,调用Java NIO接口在该端口上select connect事件,获取新建的连接后,封装成IoSession,交由后面的Processor线程处理。在客户端,也有一个类似的,叫Connector的线程与之相对应。这两类线程的数量只有1个,外界无法控制这两类线程的数量。
2.Processor
Processor线程主要负责具体的IO读写操作和执行后面的IoFilterChain和IoHandler逻辑。Processor线程的数量N默认是CPU数量+1,可以通过配置参数来控制其数量。前面进来的IoSession会被分配到这N个Processor线程中。
每个Porcessor线程中都维护着一个selector,对它维护的IoSession集合进行select,然后对select的结果进行遍历,逐一处理。像前面提到的,读取数据,以事件的形式通知后面IoFilterChain;以及对写请求队列的flush操作,都是在这类线程中来做的。
通过将session均分到多个Processor线程里进行处理,可以充分利用多核的处理能力,减轻select操作的压力。默认的Processor的线程数量设置可以满足大部分情况下的需求,但进一步的优化则需要根据实际环境进行测试。
Processor具体操作:
(1)把新添加进来的session注册到当前processor的Selector里面的 read事件,并初始化session;
(2)判断当前Selector是否有读写事件;
(3)、若第2步有读事件时,进入步骤4,若没有的话,直接到第6步;
(4)处理当前读事件,并把处理后的数据放入到flush队列里面;
(5)把第4步执行的结果flush到客户端;
(6)处理session,比如session idle时间等。
(7)重新执行第1步,循环执行。
为什么采取多线程:
如果只有单线程的话,只有当前面的IO请求处理完毕后,才会取下一个IO请求进行处理。也就是说,如果IoFilter或IoHandler中有比较耗时的操作的话(如:读取数据库等),Processor线程将会被阻塞住,这样的话对工作效率的影响就太大了。
线程池应该添加的位置:
在IoFilterChain中加入了Thread Pool Filter。这样的操作是的Processor不会阻塞提高了工作效率。开发者也可以在chain中加入多个ExecutorFilter,但很少有人这样操作。
3.Session
session做为一个连接的具体对象,缓存当前连接用户的一些信息,session对象是绑定在SelectableChannel的一个attach。
二、MINA 网络库如何使用
1.使用MINA步骤:
(1):创建一个NioSocketAcceptor对象;
(2):为NioSocketAcceptor对象添加Filter对象,这个Filter可以认为是二进制数据与对象之间的转换器,可以设置多个,可以使用框架自带的,我们也可以自己写,在这里我们使用框架自带的;
(3):创建一个继承自IoHandlerAdapter的业务逻辑处理类MinaServerHandler,并且重写IoHandlerAdapter里面的方法,我们这个示例中将仅仅是打印一些输出信息;
(4):将MinaServerHandler对象设置到NioSocketAcceptor对象上面;
(5):为当前NioSocketAcceptor绑定IP以及端口号;
2.常用接口的使用
1.IoService:
用于提供连接,他是服务器端的IoAcceptor;提供IO 服务和管理IoSession的功能。相当于是Mina的Socket层,负责所有SocketIO事件的注册,select,分发等。它位于org.apache.mina.core.service包内,它有两个子接口,表示Server端接收方的IoAcceptor和Client发起方的IoConnector,以及所有的实现类
2.IoAcceptor:
这个接口是TCPServer 的接口,主要增加了void bind()监听端口、void unbind()解除对套接字的监听等方法。这里与传统的Java 中的ServerSocket 不同的是IoAcceptor 可以多次调用bind()方法(或者在一个方法中传入多个SocketAddress 参数)同时监听多个端口。
3.IoConnector:
这个接口是TCPClient 的接口,主要增加了ConnectFuture connect(SocketAddressremoteAddress,SocketAddress localAddress)方法,用于与Server 端建立连接,第二个参数如果不传递则使用本地的一个随机端口访问Server 端。这个方法是异步执行的,同样的,也可以同时连接多个服务端。
4.IoSession:
在每一次连接建立成功之后都会创建一个IoSession对象出来,并且在创建该对象的时候创建一个IoFilter对象出来,通过IoSession的session id来为当前IoSession设置处理他的IoProcessor;
5.IoProcessor:
用于检查是否有数据在通道上面进行读写,在我们创建Acceptor或者Connector的时候,默认会创建一个线程池,里面存储的就是IoProcessor线程,该线程里面是拥有自己的Selector的,这个是MINA为我们做的一点优化,我们通常使用NIO的话是只有一个Selector的,而MINA中的
6.IoFilter:
用于定义拦截器,这些拦截器可以包括日志输出、数据编解码等等,只要用于二进制数据和对象之间的转换;
IoBuffer:消息缓存区;
8.IoHandler:
处于IoFilter的尾部,用于真正的业务逻辑处理,所以我们在使用MINA的时候是必须要提供IoHandler对象的,因为是靠他来进行真正业务处理的;这个接口是你编写业务逻辑的地方,读取数据、发送数据基本都在这个接口总完成
9.IoBuffer:
10.IoFuture:
在Mina 的很多操作中,你会看到返回值是XXXFuture,实际上他们都是IoFuture 的子类,看到这样的返回值,这个方法就说明是异步执行的,主要的子类有ConnectFuture、CloseFuture 、ReadFuture 、WriteFuture 。
三、MINA网络库的细节处理
1、粘包与段包
粘包:指TCP协议中,发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。造成的可能原因:发送端需要等缓冲区满才发送出去,造成粘包 接收方不及时接收缓冲区的包,造成多个包接收
断包:也就是数据不全,比如包太大,就把包分解成多个小包,多次发送,导致每次接收数据都不全。
2、消息传输的格式
消息长度+消息头+消息体即前N个字节用于存储消息的长度,用于判断当前消息什么时候结束。
3、编码与解码
在MINA中对象的编码与解码用的都是JDK提供的ObjectOutputStream来实现的。
编码:即把我们的消息编码成二进制形式,能以字节的形式在网络中传输。
解码:即把我们收到的字节解码成我们代码中的对象。
4、MINA中消息的处理实现
消息的接受处理,我们常用的是TCP协议,而TCP协议会分片的,在下面的代码中,具体功能就是循环从通道里面读取数据,直到没有数据可读,或者buffer满了,然后就把接受到的数据发给解码工厂进行处理。