好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Mina框架研究

Mina框架研究

Apache MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络应用程序提供了非常便利的框架。

这个框架的优点:

– 异步 
– 无阻塞 
– 事件驱动 
– 支持TCP, UDP, APR, 串口… 
– 通过 过滤器(Filters)实现扩展性 
– 同时提供协议框架

总体框架

之前的一个项目用到了MINA,最近想再系统的整理一下,主要参考 MINA 2.0 User Guide

基于MINA框架的应用程序架构应该是这样的:

底层是基于JAVA的NIO 1.0实现的;

其核心部分架构是这样的:

内部可以分为3 个层次:

I/O Service  - 执行实际的I / O,可以选择现成的Services如 (*Acceptor),也可以自己写。

I/O Filter Chain  - 这是一个由多个过滤器组成的过滤器链,在这个环节将字节数据转换到特定的数据结构中(Filters/Transforms bytes into desired Data Structures and vice-versa) 
I/O Handler  - 实际的业务逻辑部分

Server端应用

对socket通信来说,使用比较广泛的是基于Server端的应用,尤其是并发规模达到一定程度后,颇具挑战性。那么我们来看一下,基于MINA框架的Server端应用:

1、IOAcceptor 监听指定的端口,处理新的网络连接;一旦一个新的连接到达后,IOAcceptor 就产生一个session,后续所有从这个IP和端口发送过来的请求就将通过这个Session被处理。

2、Session创建后,后续所有的数据包都被人到过滤器链中,通过过滤器将原始的字节码转变成高层的对象,这个环节PacketEncoder/Decoder就十分有用。

3、最后数据包或对象被传送给Handler做业务逻辑处理;

Main.java:

 public   class  Main {
private static final int PORT = 9123;
/**
* @param args the command line arguments
*/
public static void main(String[] args) throws IOException {

IoAcceptor acceptor = new NioSocketAcceptor();
acceptor.getFilterChain().addLast( "logger" , new LoggingFilter() );
acceptor.getFilterChain().addLast( "codec" , new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));

acceptor.setHandler( new TimeServerHandler() );

acceptor.getSessionConfig().setReadBufferSize( 2048 );
acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );
acceptor.bind( new InetSocketAddress(PORT));

}

}

1、创建IoAcceptor;

2、加入日志记录和解码的过滤器,其中日志过滤器用SL4J库记录信息,而编码过滤器则解码所有收到的信息。使用 new TextLineCodecFactory() 发送的信息迕行编码,返是MINA自带的,功能有限,只能处理文本戒者String类型。

3、设置ServerHandler,这里是一个自定义的Handler:TimeServerHandler;

4、设置Session的对应的I/O processor 读缓存区大小2048;通常这个参数不需要设置;

5、设置空闲时间,这里的 BOTH_IDLE 指 EADER_IDLE  和  WRITER_IDLE . 都为10秒;

6、绑定监听端口9123;

TimeServerHandler.java:

 /* 
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package minatest1;

/**
*
* @author THINKPAD
*/

import java.util.Date;

import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;

public class TimeServerHandler extends IoHandlerAdapter
{
@Override
public void exceptionCaught( IoSession session, Throwable cause ) throws Exception
{
cause.printStackTrace();
}

@Override
public void messageReceived( IoSession session, Object message ) throws Exception
{
String str = message.toString();
if ( str.trim().equalsIgnoreCase( "quit" ) ) {
session.close();
return ;
}

Date date = new Date();
session.write( date.toString() );
System.out.println( "Message written..." );
}

@Override
public void sessionIdle( IoSession session, IdleStatus status ) throws Exception
{
System.out.println( "IDLE " + session.getIdleCount( status ));
}
}

这里主要有一下几个主要的方法:

messageReceived(…),对接收到的消息(已经解码)迕行下一步处理,这里对收到的字符串进行判断,如果是”quit”则断开连接;否则输出当前时间的字符串格式;

exceptionCaught(…),自定义异常处理, 要不然异常会被“吃掉”;

sessionIdle,当Session处于IDLE状态的时候,输出空闲状态次数;

测试,输入:telnet 127.0.0.1 9123,随便输入一串字符串,显示当前的时间:

IoService

IoService是一个接口,有两种实现:IoAcceptor和IoConnector;其中IoAcceptor是针对Server端的实现,IoConnector是针对Client端的实现;IoService的职责包括:

1、监听器管理

2、IoHandler

3、IoSession管理

4、FilterChain管理

5、Statistics管理

IoAcceptor

主要用于创建新的连接。MINA提供了多种实现,所以几乎不需要我们自己再去实现:

NioSocketAcceptor: 无阻塞的Socket 传输Acceptor,针对TCP

NioDatagramAcceptor  : 无阻塞的Socket 传输Acceptor,针对UDP

AprSocketAcceptor  : 阻塞的Socket 传输Acceptor,基于 APR

VmPipeSocketAcceptor  : the in-VM Acceptor

IoConnector

针对Client端的Socket连接,有多种实现:

NioSocketConnector  : 无阻塞的Socket 传输Connector,针对TCP 
NioDatagramConnector  : 无阻塞的Socket 传输Connector,针对UDP 
AprSocketConnector  : 阻塞的Socket 传输Connector,基于 APR 

ProxyConnector  : 一个支持代理服务的 Connector ,通过截取连接的请求,并将终端指向代理设置的地址。

SerialConnector  : 针对串口传输的Connector

VmPipeConnector  : the in-VM * Connector*

Session

任何时候只要有新的连接到来,都会生成一个Session对象,并且一致保存在内存中,只到连接断开;

Session有一系列状态,如下:

Connected : session被创建,并有效 
Idle : session至少在一个空闲周期(见配置)内没有处理过任何请求 
Idle for read : 在一个空闲周期内没有做实际的读操作

Idle for write : 在一个空闲周期内没有做实际的写操作

Idle for both : 在一个空闲周期内没有做实际的读和写操作 
Closing :session正在被关闭

Closed : session已经被关闭

IoBuffer

IoBuffer是MINA内部使用的一个byte buffer,MINA并没有直接使用NIO 的ByteBuffer。不过IoBuffer 是对 ByteBuffer 的一个封装。IoBuffer 中的很多方法都是对 ByteBuffer 的直接继承。只是对 ByteBuffer 添加了一些扩展了更加实用的方法。

基本用法

由于IoBuffer是对Nio的ByteBuffer 的封装,所以基本概念还是相同的,下面简单介绍一下:

1、 capacity :该属性描述这个缓冲区最多能缓冲多少个元素,也是Buffer最大存储元素数,这个值是在创建Buffer的时候指定的,且不能修改。

2、 Limit: 在从Buffer中向Channel中写数据时,limit变量指示了还剩多少数据可以读取,在从Channel中读取数据到Buffer中时,limit变量指示了还剩多少空间可供存放数据。position正常情况下小于或者等于limit。

3、 Position: Buffer实际上也就是个array。当你从Channel中读数据时,你把从Channel中读出来的数据放进底层array,position变量用来跟踪截止目前为止已经写了多少数据。更精确的讲,它指示如果下次写Buffer时数据应该进入array的哪个位置。因此如果已经从Channel中读出了3个字节,Buffer的position会被置为3,指向array中第四个位置。

4、Mark:一个可以记忆的Position位置的值,在调用reset()方法时会将缓冲区的Position重置为该索引,并非总是需要定义Mark,但是在定义Mark时,不能将其定义为负数,并且不能让它大于Position,如果定义了Mark,则在该Position或Limit调整为小于该Mark值时,该Mark将被丢弃。

下面通过一个例子来说明:

i、初始状态下:

此时position为0,limit和capacity都被设为9;

ii、从Channel中读入4个字节数据到Buffer,这时position指向4(第5个):

iii、在做写操作之前,我们必须调用一次flip()方法,这个方法做了两件重要的事情: 
1. 将limit设置到当前的position处。 
2. 设置position为0。

iiii、执行写操作后;

iv、执行clear后,position设为0,limit设为capition,mark则丢弃;

因为IoBuffer是一个抽象类,不能直接实例化,所有使用的时候需要调用allocate方法来进行内存分配;

allocate有两种定义:

    1:   // Allocates a new buffer with a specific size, defining its type (direct or heap) 
    2:   public   static  IoBuffer allocate( int  capacity,  boolean  direct)
    3:   
    4:   // Allocates a new buffer with a specific size 
    5:   public   static  IoBuffer allocate( int  capacity)

这里:

capacity:buffer的大小;

direct:如果为 true, 则得到 direct buffer, 如果为false,则得到 heap buffer

direct buffer和 heap buffer的区别分析:

Direct Buffer不是分配在堆上的,它不被GC直接管理(但Direct Buffer的JAVA对象是归GC管理的,只要GC回收了它的JAVA对象,操作系统才会释放Direct Buffer所申请的空间),它似乎给人感觉是“内核缓冲区(buffer in kernel)”。Heap Buffer则是分配在堆上的,或者我们可以简单理解为Heap Buffer就是byte[]数组的一种封装形式。当我们把一个Heap Buffer写入Channel的时候,实际上底层实现会先构建一个临时的Direct Buffer,然后把Heap Buffer的内容复制到这个临时的Direct Buffer上,再把这个Direct Buffer写出去。因此把一个Direct Buffer写入一个Channel的速度要比把一个Heap Buffer写入一个Channel的速度要快。但是Direct Buffer创建和销毁的代价很高,所以要用在尽可能重用的地方。

public static IoBuffer allocate(int capacity)的用法:

    1:   // 设置Allocates分配的默认类型,这里设为heap buffer. 
    2:   IoBuffer.setUseDirectBuffer(false);
    3:    // 返回一个新的heap buffer. 
    4:   IoBuffer buf = IoBuffer.allocate(1024);

IoBuffer允许生成一个自动扩展的buffer(这也是没有选择使用NIO的ByteBuffer的原因之一);通过设置AutoExpand属性即可:

    1:  IoBuffer buffer = IoBuffer.allocate(8);
    2:  buffer.setAutoExpand(true);
    3:   
    4:  buffer.putString( "12345678" , encoder);
    5:         
    6:   // Add more to this buffer 
    7:  buffer.put(( byte )10);

 

分类:  Java

标签:  MINA

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于Mina框架研究的详细内容...

  阅读:44次