Mina工做原理分析

  Mina是Apache社区维护的一个开源的高性能IO框架,在业界内久经考验,广为使用。Mina与后来兴起的高性能IO新贵Netty同样,都是韩国人Trustin Lee的大做,两者的设计理念是极为类似的。在做为一个强大的开发工具的同时,这两个框架的优雅设计和不俗的表现,有不少地方是值得学习和借鉴的。本文将从Mina工做原理的角度出发,对其结构进行分析。html

 1、整体结构数据库

 Mina的底层依赖的主要是Java NIO库,上层提供的是基于事件的异步接口。其总体的结构以下:apache

 

IoService

  最底层的是IOService,负责具体的IO相关工做。这一层的典型表明有IOSocketAcceptor和IOSocketChannel,分别对应TCP协议下的服务端和客户端的IOService。IOService的意义在于隐藏底层IO的细节,对上提供统一的基于事件的异步IO接口。每当有数据到达时,IOService会先调用底层IO接口读取数据,封装成IoBuffer,以后以事件的形式通知上层代码,从而将Java NIO的同步IO接口转化成了异步IO。因此从图上看,进来的low-level IO通过IOService层后变成IO Event。服务器

  具体的代码能够参考org.apache.mina.core.polling.AbstractPollingIoProcessor的私有内部类Processor。网络

IoFilterChain

  Mina的设计理念之一就是业务代码和数据包处理代码分离,业务代码只专一于业务逻辑,其余的逻辑如:数据包的解析,封装,过滤等则交由IoFilterChain来处理。IoFilterChain能够当作是Mina处理流程的扩展点。这样的划分使得结构更加清晰,代码分工更明确。开发者经过往Chain中添加IoFilter,来加强处理流程,而不会影响后面的业务逻辑代码。session

IoHandler

 IoHandler是实现业务逻辑的地方,须要有开发者本身来实现这个接口。IoHandler能够当作是Mina处理流程的终点,每一个IoService都须要指定一个IoHandler。数据结构

IoSession

 IoSession是对底层链接的封装,一个IoSession对应于一个底层的IO链接(在Mina中UDP也被抽象成了链接)。经过IoSession,能够获取当前链接相关的上下文信息,以及向远程peer发送数据。发送数据其实也是个异步的过程。发送的操做首先会逆向穿过IoFilterChain,到达IoService。但IoService上并不会直接调用底层IO接口来将数据发送出去,而是会将该次调用封装成一个WriteRequest,放入session的writeRequestQueue中,最后由IoProcessor线程统一调度flush出去。因此发送操做并不会引发上层调用线程的阻塞。多线程

具体代码能够参考org.apache.mina.core.filterchain.DefaultIoFilterChain的内部类HeadFilter的filterWrite方法。并发

参见:http://www.cnblogs.com/xuekyo/archive/2013/03/06/2945826.html框架

          http://www.cnblogs.com/metoy/p/3593264.html

2、工做流程

整体来说Mina框架分3层:

  • I/O Service :负责处理I/O,执行IO操做;
  • I/O Filter Chain :过滤链。负责编码处理,字节到数据结构或数据结构到字节的转换等,即非业务逻辑的操做
  • I/O Handler :负责处理业务逻辑

因此要建立一个基于NIMA框架的应用程序,必须:

  1. Create an I/O service - 建立一个已经(*Acceptor)服务
  2. Create a Filter Chain - 建立一系列的Filters并加入到过滤链
  3. Create an I/O Handler - 实现本身的业务逻辑

客户端的主要逻辑思路以下:

  •  客户端首先建立一个IOConnector 用来和服务端通讯,顾名思义这就是创建的一个链接对象;
  • 在这个链接上建立一个session, 客户端中的业务方法能够向session中写入数据,数据通过Filter Chain的过滤后会发送给服务端;
  • 从服务端发回的数据也会首先通过Filter Chain的过滤,而后交给IOHandler作进一步的处理

服务端的主要逻辑思路以下:

  • IOAcceptor 监听网络数据包传入的链接;
  • 为每一个新的链接(Connection)建立一个session,同一个端口+ip的后续请求将经过session进行处理;
  • 同一个session收到的全部数据,经过过滤链进行过滤.经过PacketEncoder/Decoder进行有效的编码,解码处理(负责把底层传输的对象拼装为更高一层的对象方便后续的处理,最后传输的数据被交给IOHandler);
  • 最后根据本身的业务需求完成Handler的业务逻辑处理.

参见:http://www.cnblogs.com/quyongjin/archive/2013/06/08/3127222.html

     经过SocketAcceptor 同客户端创建链接; 

     链接创建以后 I/O的读写交给了I/O Processor线程,I/O Processor是多线程的; 

     经过I/O Processor 读取的数据通过IoFilterChain里全部配置的IoFilter, IoFilter  进行消息的过滤,格式的转换,在这个层面能够制定一些自定义的协议; 

     最后  IoFilter 将数据交给 Handler  进行业务处理,完成了整个读取的过程; 

     写入过程也是相似,只是恰好倒过来,经过IoSession.write 写出数据,而后Handler进行写入的业务处理,处理完成后交给IoFilterChain,进行消息过滤和协议的转换,最后经过 I/O Processor 将数据写出到 socket 通道。

 3、工做原理

  Mina里面是怎么使用Java NIO和进行线程调度的呢?这是提升IO处理性能的关键所在。Mina的线程调度原理主要以下图所示:

Acceptor与Connector线程

  在服务器端,bind一个端口后,会建立一个Acceptor线程来负责监听工做。这个线程的工做只有一个:调用Java NIO接口在该端口上select connect事件,获取新建的链接后,封装成IoSession,交由后面的Processor线程处理。

 在客户端,也有一个相似的,叫Connector的线程与之相对应。这两类线程的数量只有1个,外界没法控制这两类线程的数量。

 TCP实现的代码能够参考org.apache.mina.core.polling.AbstractPollingIoAcceptor的内部类Acceptor和org.apache.mina.core.polling.AbstractPollingIoConnector的内部类Connector。

Processor线程

 Processor线程主要负责具体的IO读写操做和执行后面的IoFilterChain和IoHandler逻辑。Processor线程的数量N默认是CPU数量+1,能够经过配置参数来控制其数量。前面进来的IoSession会被分配到这N个Processor线程中。默认的SimpleIoProcessorPool的策略是session id绝对值对N取模来分配。

 每一个Porcessor线程中都维护着一个selector,对它维护的IoSession集合进行select,而后对select的结果进行遍历,逐一处理。像前面提到的,读取数据,以事件的形式通知后面IoFilterChain;以及对写请求队列的flush操做,都是在这类线程中来作的。

 经过将session均分到多个Processor线程里进行处理,能够充分利用多核的处理能力,减轻select操做的压力。默认的Processor的线程数量设置能够知足大部分状况下的需求,但进一步的优化则须要根据实际环境进行测试。

4、线程模型

线程模型原理

  单一的Processor线程内部来看,IO请求的处理流程是单线程顺序处理的。前面也提到过,当Process线程select了一批就绪的IO请求后,会在线程内部逐一对这些IO请求进行处理。处理的流程包括IoFilter和IoHandler里的逻辑。当前面的IO请求处理完毕后,才会取下一个IO请求进行处理。也就是说,若是IoFilter或IoHandler中有比较耗时的操做的话(如:读取数据库等),Processor线程将会被阻塞住,后续的请求将得不处处理。这样的状况在高并发的服务器下显然是不能容忍的。因而,Mina经过在处理流程中引入线程池来解决这个问题。

 那么线程池应该加在什么地方呢?正如前面所提到过的:IoFilterChain是Mina的扩展点。没错,Mina里是经过IoFilter的形式来为处理流程添加线程池的。Mina的线程模型主要有一下这几种形式:

 

第一种模型是单线程模型,也是Mina默认线程模型。也就是Processor包办了从底层IO到上层的IoHandler逻辑的全部执行工做。这种模型比较适合于处理逻辑简单,能快速返回的状况。

第二种模型则是在IoFilterChain中加入了Thread Pool Filter。此时的处理流程变为Processor线程读取完数据后,执行IoFilterChain的逻辑。当执行到Thread Pool Filter的时候,该Filter会将后续的处理流程封装到一个Runnable对象中,并交由Filter自身的线程池来执行,而Processor线程则能当即返回来处理下一个IO请求。这样若是后面的IoFilter或IoHandler中有阻塞操做,只会引发Filter线程池里的线程阻塞,而不会阻塞住Processor线程,从而提升了服务器的处理能力。Mina提供了Thread Pool Filter的一个实现:ExecutorFilter。

固然,也没有限制说chain中只能添加一个ExecutorFilter,开发者也能够在chain中加入多个ExecutorFilter来构成第三种状况,但通常状况下可能没有这个必要。

请求的处理顺序

 在处理流程中加入线程池,能够较好的提升服务器的吞吐量,但也带来了新的问题:请求的处理顺序问题。在单线程的模型下,能够保证IO请求是挨个顺序地处理的。加入线程池以后,同一个IoSession的多个IO请求可能被ExecutorFilter并行的处理,这对于一些对请求处理顺序有要求的程序来讲是不但愿看到的。好比:数据库服务器处理同一个会话里的prepare,execute,commit请求但愿是能按顺序逐一执行的。

 Mina里默认的实现是有保证同一个IoSession中IO请求的顺序的。具体的实现是,ExecutorFilter默认采用了Mina提供的OrderedThreadPoolExecutor做为内置线程池。后者并不会当即执行加入进来的Runnable对象,而是会先从Runnable对象里获取关联的IoSession(这里有个down cast成IoEvent的操做),并将Runnable对象加入到session的任务列表中。OrderedThreadPoolExecutor会按session里任务列表的顺序来处理请求,从而保证了请求的执行顺序。

 对于没有顺序要请求的状况,能够为ExecutorFilter指定一个Executor来替换掉默认的OrderedThreadPoolExecutor,让同一个session的多个请求能被并行地处理,来进一步提升吞吐量。

相关文章
相关标签/搜索