Mina总结

Mina

MINA(IO和处理逻辑共用一个线程,对于handler处理逻辑比较复杂耗时长的来说,需要给handler一个单独的线程来处理,也就是io处理线程和handler线程分开,ioservice和ExecutorFilter分开):

服务端,
1、创建NIOSocketAcceptor
2、bind绑定地址端口,启动一个线程Accept线程,轮询是否有新的端口绑定//应用端发送注册accept事件,
    在轮询(runable,while)过程中,把ServerSocketChannel和地址绑定,并对这些channel在selector器注册Accept事件;
进行select操作Selector.select( ),如果有accept事件发生,那么就注册一个新的连接session(包含处理的IOProcessor,session信息等)--一个accept线程(每一个bind一个线程,循环做连接监听用,该accept线程放到Executor中执行)
3、在run I/O Processor (多线程,Runable--Excutor,轮询while)的过程中,如果session是新的(accept),初始化session(在selector上为该channel注册READ事件),初始化IOfilter。
  如果select中有事件(READ,WRITE),那么就进行I/O处理
  -----IO处理线程池(Runable--Excutor),处理I/O,经过ioprocessor读取数据后,交给IO filter进行进行消息的过滤,格式的转换。完成后交给逻辑handler处理
4、在handler中处理逻辑,处理完成后,由用户调用session的write进行结果返回并注册write事件(交给iofilter进行消息的协议转换过滤,后通过IO processor写出到socket通道操作系统缓冲区)
发生Write事件时,望flush队列中发送数据

 
 
客户端,
SocketConnector
客户端通信过程 
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贯穿整个通信过程的始终 

客户端同步调用:
  1. client一个线程调用远程接口,生成一个唯一的ID(比如一段随机字符串,UUID等),Dubbo是使用AtomicLong从0开始累计数字的
  2. 将打包的方法调用信息(如调用的接口名称,方法名称,参数值列表等),和处理结果的回调对象callback,全部封装在一起,组成一个对象object
  3. 向专门存放调用信息的全局ConcurrentHashMap里面put(ID, callback)
  4. 将ID和打包的方法调用信息封装成一对象connRequest,使用IoSession.write(connRequest)异步发送出去
  5. 当前线程再使用callback的get()方法试图获取远程返回的结果,在get()内部,则使用synchronized获取回调对象callback的锁, 再先检测是否已经获取到结果,如果没有,然后调用callback的wait()方法,释放callback上的锁,让当前线程处于等待状态。
  6. 服务端接收到请求并处理后,将结果(此结果中包含了前面的ID,即回传)发送给客户端,客户端socket连接上专门监听消息的线程收到消息,分析结果,取到ID,再从前面的ConcurrentHashMap里面get(ID),从而找到callback,将方法调用结果设置到callback对象里。
  7. 监听线程接着使用synchronized获取回调对象callback的锁(因为前面调用过wait(),那个线程已释放callback的锁了),再notifyAll(),唤醒前面处于等待状态的线程继续执行(callback的get()方法继续执行就能拿到调用结果了),至此,整个过程结束

序列化机制:
默认采用java自己的序列化机制
可以扩展采用hession序列化,效率高一些


长连接的连接池(具有负载均衡,连接失效重连机制)
客户端初始化几个 IoSession连接,放到连接池中,调用的时候,随时可以用。
   pop,push方法
并且提供连接的心跳机制,当连接失效时,需要重新建立连接信息


优化点(引用):

在使用Mina 2.0之前的版本时,以下几个方面值得注意:

使用自定义的ThreadModel

通过SocketConnectorConfig.setThreadModel(ThreadModel.MANUAL)将线程模式改为自定义模式,否则Mina会自行启动一个最大线程数为16个的线程池来处理具体的消息,这对于多数应用而言都不适用,因此最好是自行控制具体处理消息的线程池。

合理配置IO处理线程池

在创建SocketAcceptor或SocketConnector时要提供一个线程池及最大的线程数,也就是Mina用于IO事件处理的线程数,通常建议将这个线程数配置为CPU核数+1。

监听是否成功写入操作系统的发送缓冲区

在调用IoSession.write时,Mina并不确保其会成功写入操作系统的发送缓冲区中(例如写入时连接刚好断开),为了确定是否成功,可采用如下方法:

        
        
        
        
  1. WriteFuture writeResult=session.write(data);  
  2. writeResult.addListener(new IoFutureListener() {  
  3.     public void operationComplete(IoFuture future){  
  4.         WriteFuture wfuture=(WriteFuture)future;  
  5.         // 写入成功  
  6.         if(wfuture.isWritten()){  
  7.             return;  
  8.         }  
  9.             // 写入失败,自行进行处理  
  10.     }  
  11. }); 

这对于同步请求而言特别重要,通常同步请求时都会设置一个等待响应的超时时间,如果不去监听是否成功写入的话,那么同步的请求一直要等到设定的超时时间才能返回。

监听写超时

当接收消息方的接收缓冲区占满时,发送方会出现写超时的现象,这时Mina会向外抛出WriteTimeoutException,如有必要,可在IoHandler实现的exceptionCaught方法里进行处理。

借助Mina IoSession上未发送的bytes信息实现流控

当IoSession上堆积了过多未发送的byte时,会造成jvm内存消耗过多的现象。因此通常要控制IoSession上堆积的未发送的byte量,此值可通过Mina IoSession的getScheduledWriteBytes来获取,从而进行流控。

messageReceived方法占用IO处理线程

在使用Thread.MANUAL的情况下,IOHandler里的messageReceived方法会占用Mina的IO处理线程,为了避免业务处理接收消息的速度影响IO处理性能,建议在此方法中另起线程来做业务处理。

序列化/反序列化过程会占用IO处理线程

由于Mina的序列化/反序列化过程是在FilterChain上做的,同样会占据IO处理线程。Mina将同一连接上需要发送和接收的消息放在队列中串行处理。如果序列化/反序列化过程耗时较长,就会造成同一连接上其他消息的接收或发送变慢。

反序列化时注意继承CumulativeProtocolDecoder

在使用NIO的情况下,每次读取的流并不一定完整,因此要通过继承CumulativeProtocolDecoder来确保当流没读完时,下次接着读,这同时也要求应用在协议头中保持此次发送流的长度信息。

Mina 1.1.6及以前的版本中sessionClosed可能会不被调用的bug

在某些高压力的情况下,当连接断开时,Mina 1.1.6及以前的版本并不会调用IoHandler中的sessionClosed方法,这对于某些要在sessionClosed做相应处理的应用来说会出现问题,这个bug 在Mina 1.1.7的版本中已修复。


你可能感兴趣的:(Mina总结)