mina中文教程

来源:互联网 发布:centos单机安装hadoop 编辑:程序博客网 时间:2024/04/26 07:33

现在已经是World Wide Web的时代,无数的web应用框架被创造出来从而大大的提高了web开发的速度。抛开WWW的这个优势,我们知道还有很多协议是HTTP协议所无法替代的。有时,我们仍然需要构造c/s应用来实现适当的协议。

=== MINA是什么? ===
你有没有曾经使用java或者其他语言实现过某个协议栈?就像你所经历过的那样,编写网络应用即使对于有经验的开发者也不是容易的事情。这归咎于以下几个方面:

   * 没有为开发者设计的合适的网络应用框架.
     * 使你无法在有限的时间内创建你的应用.
   * 网络I/O编码,消息的编/解码,业务逻辑常常纠缠在一起.
     * 使程序失去可维护性和可复用性
   * 网络应用难于进行单元测试
     * 你失去了敏捷性

MINA是一个网络应用框架,在不牺牲性能和可扩展性的前提下用于解决上面的所有问题。


== I/O 层: 编写一个 Echo Server ==
MINA包含两层:IO层和协议层。我们首先仅使用IO层来实现一个echo服务,因为协议层通常是建立在IO层之上的。
attachment:Arch1.gif
上面的图展示了MINA的IO层同客户端的交互。IoAcceptor执行所有底层IO,将他们翻译成抽象的IO事件,并把翻译过的事件和关联的IoSession
发送给IoHandler。


=== IoSession ===
attachment:IoSession.gif
一个代表了IoSession程序同一个远程实体的IO连接。通过IoSession,你可以写出message到远程实体,访问session的配置,并且更改session的属性。


=== IoHandler ===
attachment:IoHandler.gif
   * sessionCreated: 当一个IO连接建立时被调用,这个方法在任何IO操作之前被调用,以便socket参数或session属性能够最先被设置。
   * sessionOpened: 在sessionCreated调用之后被调用。
   * sessionClosed: 当IO连接被关闭时被调用。
   * sessionIdle: 当在远程实体和用户程序之间没有数据传输的时候被调用。
   * exceptionCaught: 当IoAcceptor 或者你的IoHandler.中出现异常时被调用。
   * messageReceived: 当接收到新的协议消息时被调用。可以在这里实现你的控制流程。
   * messageSent: 当用户请求的消息通过 IoSession#write(Object) 确实发送后被调用。


下面我们看看如何实现echo协议的IoHandler。
=== 实现 IoHandler 以及启动代码 ===
通常,应用需要继承IoHandlerAdapter并实现需要的方法:


packageorg.apache.mina.examples.echoserver;

importorg.apache.mina.common.ByteBuffer;
importorg.apache.mina.common.IoHandlerAdapter;
importorg.apache.mina.common.IoSession;
importorg.apache.mina.common.TransportType;
importorg.apache.mina.transport.socket.nio.SocketSessionConfig;

publicclassEchoProtocolHandler extendsIoHandlerAdapter
{
    
publicvoidsessionCreated( IoSession session )
    
{
        
if(session.getTransportType() ==TransportType.SOCKET) {
            ((SocketSessionConfig)session.getConfig()).setReceiveBufferSize(
2048);
      }


    
publicvoidexceptionCaught( IoSession session, Throwable cause )
    
{
          session.close();
      }


    
publicvoidmessageReceived( IoSession session, Object message )
    
{
        
if(!(messageinstanceofByteBuffer))
            
return;
     
          ByteBuffer rb
=(ByteBuffer)message;
        
//Write the received data back to remote peer
          ByteBuffer wb=ByteBuffer.allocate( rb.remaining() );
          wb.put( rb );
          wb.flip();
          session.write( wb );
      }

}

   刚刚我们使用MINA实现echo协议,现在我们将handler绑定到一个server端口上。
importjava.net.InetSocketAddress;
importorg.apache.mina.transport.socket.nio.SocketAcceptor;
importorg.apache.mina.transport.socket.nio.SocketAcceptorConfig;

publicclassMain
{
    
/**Choose your favorite port number. */
    
privatestaticfinalintPORT =8080;
    

    
publicstaticvoidmain( String[] args ) throwsException
    
{
          SocketAcceptor acceptor
=newSocketAcceptor();
          SocketAcceptorConfig defaultConfig
=newSocketAcceptorConfig();
          defaultConfig.setReuseAddress(
true);
     
        
//Bind
          acceptor.bind(newInetSocketAddress(PORT), newEchoProtocolHandler(), defaultConfig);

          System.out.println(
"Listening on port"+PORT );
      }

}


=== 添加IoFilters ===
IoFilter提供了更加有力的方式来扩展MINA。它拦截所有的IO事件进行事件的预处理和后处理。你可以把它想象成Servlet的filters。IoFilter能够实现以下几种目的:

   * 事件日志
   * 性能检测
   * 数据转换(e.g. SSL support)
   * 防火墙…等等
attachment:Arch2.gif
我们的echo协议handler不对任何IO事件进行日志。我们可以通过添加一个filter来增加日志能力。MINA提供了IoLoggingFilter来进行日志。我们只要添加日志filter到ServiceRegistry即可。
.
DefaultIoFilterChainBuilder chain
=config.getFilterChain();
addLogger(chain);
.

privatestaticvoidaddLogger( DefaultIoFilterChainBuilder chain ) throwsException
{
      chain.addLast(
"logger",newLoggingFilter() );
}


想使用SSL?MINA也提供了一个SSL的filter,但它需要JDK1.5。
.
DefaultIoFilterChainBuilder chain
=config.getFilterChain();
addLogger(chain);
.

privatestaticvoidaddSSLSupport( DefaultIoFilterChainBuilder chain )
    
throwsException
{
      SSLFilter sslFilter
=
        
newSSLFilter( BogusSSLContextFactory.getInstance( true) );
      chain.addLast(
"sslFilter", sslFilter );
      System.out.println(
"SSL ON");
}

== 协议层: 实现反转Echo协议 ==
在上面我们通过简单的echo server的例子学习了如何使用IO层,但是如果想实现复杂的如LDAP这样的协议怎么办呢?它似乎是一个恶梦,因为IO层没有帮助你分离‘message解析’和‘实际的业务逻辑(比如访问一个目录数据库)’。MINA提供了一个协议层来解决这个问题。协议层将ByteBuffer事件转换成高层的POJO事件:

attachment:Arch3.gif

使用协议层必须实现5个接口:ProtocolHandler, ProtocolProvider, ProtocolCodecFactory,
ProtocolEncoder, 和 ProtocolDecoder:

attachment:ProtocolClasses.gif

可能看上去有点麻烦,但是请注意ProtocolCodecFactory, ProtocolEncoder, 和
ProtocolDecoder是可以完全复用的;Apache的ASN1项目为MINA提供了ASN.1解码器,更通用的解码器如:XML、java对象序列化和简单的文本将在MINA的下一个版本中提供。一旦你实现了一个灵活的解码器,你可以在未来的应用中复用它,即使你不打算复用你的解码器,MINA也提供了一个很简单的方法来实现复杂的协议。(请参考高级主题)
在这一章中,我们添加一个‘反转’server,它用于反转它接到的所有文本,我们通过它来示范如何编写一个协议层。



=== ProtocolSession ===
attachment:ProtocolSession.gif
ProtocolSession同IO层的IoSession同样继承自Session。就像前面提到的,你只需撰写面向POJO的message而不是ByteBuffer的。ProtocolEncoder
将message对象解释成ByteBuffers以便IO层能够将他们输出到socket。=== ProtocolHandler ===
ProtocolHandler类似于IO层的IoHandler.dataRead和dataWritten方法被替换成messageReceived和messageSent。这是因为ProtocolDecoder
已经将IO层接收到的 ByteBuffers转换成了message对象。
packageorg.apache.mina.examples.reverser;

importorg.apache.mina.common.IoHandler;
importorg.apache.mina.common.IoHandlerAdapter;
importorg.apache.mina.common.IoSession;

publicclassReverseProtocolHandler extendsIoHandlerAdapter
{
    
publicvoidexceptionCaught( IoSession session, Throwable cause )
    
{
        
//Close connection when unexpected exception is caught.
          session.close();
      }


    
publicvoidmessageReceived( IoSession session, Object message )
    
{
        
//Reverse received string
          String str=message.toString();
          StringBuffer buf
=newStringBuffer( str.length() );
        
for(inti =str.length()  1; i>=0; i )
        
{
              buf.append( str.charAt( i ) );
          }


        
//and write it back.
          session.write( buf.toString() );
      }

}


=== ProtocolEncoder 和 ProtocolDecoder ===
attachment:ProtocolCodec.gif
ProtocolEncoder 和ProtocolDecoder只有一个方法。ProtocolEncoder将message对象转换成一个ByteBuffer,而ProtocolDecoder将一个ByteBuffer转换成message对象。下面我们将学习如何实现这些接口。要实现反转协议要实作的唯一接口就是ProtocolProvider。它非常简单:
(注:Provider用于在一个统一的类中提供该协议相关的Handler、Decoder和Encoder。)

packageorg.apache.mina.examples.reverser;

importorg.apache.mina.protocol.*;

/**
   * {
@linkProtocolProvider} implementation for reverser server protocol.
*/

publicclassReverseProtocolProvider implementsProtocolProvider
{
    
//Protocol handler is usually a singleton.
    privatestaticProtocolHandler HANDLER =
newReverseProtocolHandler();

    
//Codec factory is also usually a singleton.
    privatestaticProtocolCodecFactory CODEC_FACTORY =
newProtocolCodecFactory()
    
{
        
publicProtocolEncoder newEncoder()
        
{
            
//Create a new encoder.
            returnnewTextLineEncoder();
          }


        
publicProtocolDecoder newDecoder()
        
{
            
//Create a new decoder.
            returnnewTextLineDecoder();
          }

      }
;

    
publicProtocolCodecFactory getCodecFactory()
    
{
        
returnCODEC_FACTORY;
      }


    
publicProtocolHandler getHandler()
    
{
        
returnHANDLER;
      }

}


这样,反转协议就被完全实现了。启动的部分同echo server非常相似:
packageorg.apache.mina.examples.reverser;

importorg.apache.mina.common.*;
importorg.apache.mina.protocol.*;
importorg.apache.mina.registry.*;

/**
   * (<b>Entry point</b>) Reverser server which reverses all text lines from
   * clients.
   *
   *
@authorTrustin Lee (trustin@apache.org)
   *
@version$Rev: 165594 $, $Date: 20050502 16:21:22 +0900 $,
 
*/

publicclassMain
{
    
privatestaticfinalintPORT =8080;

    
publicstaticvoidmain( String[] args ) throwsException
    
{
          ServiceRegistry registry
=newSimpleServiceRegistry();

        
//Bind
          Service service=newService( "reverse", TransportType.SOCKET, PORT );
          registry.bind( service,
newReverseProtocolProvider() );

          System.out.println(
"Listening on port"+PORT );
      }

}


=== 添加 ProtocolFilters ==
ProtocolFilter 同IO层的IoFilter类似:
attachment:Arch4.gif

添加IoLoggingFilter来记录底层IO事件是为了debug。我们可以用ProtocolLoggingFilter代替它来记录高层事件:

privatestaticvoidaddLogger( ServiceRegistry registry )
    
{
          ProtocolAcceptor acceptor
=registry.getProtocolAcceptor( TransportType.SOCKET );
          acceptor.getFilterChain().addLast(
"logger",newProtocolLoggingFilter() );
          System.out.println(
"Logging ON");
      }


=== ByteBuffers ===

MINA没有直接使用使用java NIO的ByteBuffer类。它使用一个自制的ByteBuffer来扩展java
NIO ByteBuffer的功能。
以下是它们的一些区别:
   * MINA ByteBuffer是一个抽象类,用户可以自由的扩展它
   * MINA 管理 MINA ByteBuffers 并对其提供对象池. Users can control the point the
buffers are released by providing acquire() and release() methods.
   * MINA ByteBuffer提供很多便利的方法,如:无符号数值的getter和基于String的getter和putter
如果你使用MINA,你将不需要直接使用NIO buffers,因为仅使用MINA buffers就可以完成大多数buffer操作。

==== ByteBuffer 池 ====

MINA有一个全局的ByteBuffer池,它被在同一个虚拟机下的所有MINA应用共享。任何分配的buffers将在IO操作或者事件处理方法被执行之后被释放。所以你可以调用ByteBuffer.allocate()来从池中得到一个ByteBuffer而不需要将它返回到池中。请查阅ByteBuffer
JavaDocs获得更多信息。

=== 线程模式 ===

MINA通过它灵活的filter机制来提供多种线程模型。没有线程池过滤器被使用时MINA运行在一个单线程模式。如果添加了一个IoThreadPoolFilter
到IoAcceptor,你将得到一个leaderfollower模式的线程池。如果再添加一个ProtocolThreadPoolFilter,你的server将有两个线程池;一个(IoThreadPoolFilter)被用于对message对象进行转换,另外一个(ProtocolThreadPoolFilter)被用于处理业务逻辑。
SimpleServiceRegistry加上IoThreadPoolFilter和ProtocolThreadPoolFilter的缺省实现即可适用于需要高伸缩性的应用。如果你想使用自己的线程模型,请查看SimpleServiceRegistry的源代码,并且自己初始化Acceptor。显然,这是个繁琐的工作。

IoThreadPoolFilter threadPool=newIoThreadPoolFilter();
threadPool.start();

IoAcceptor acceptor
=newSocketAcceptor();
acceptor.getFilterChain().addLast(
"threadPool", threadPool );

ProtocolThreadPoolFilter threadPool2
=newProtocolThreadPoolFilter();
threadPool2.start();

ProtocolAcceptor acceptor2
=newIoProtocolAcceptor( acceptor );
acceptor2.getFilterChain().addLast(
"threadPool", threadPool2 );



threadPool2.stop();
threadPool.stop();



=== 更复杂的协议支持 ===

‘Reverser’示例相对于其他复杂的协议来说仍然过于简单。要想让一个server工作,仍然有许多message类型和它们的转换的工作需要作。MINA提供了一下工具类来提供帮助:

   * DemuxingProtocolHandler
   * DemuxingProtocolCodecFactory

更多细节请参考 JavaDocs 。


   === VM 内部管道通讯 ===   你一定已经知道协议层是建立在IO层之上的,但是有时也不一定。虽然我们通常使用协议层来包装IO层,但仍有一种特殊的协议层实现,称作:’inVM pipe communication’ 让我们假设你需要使用MINA实现一个SMTP server和一个Spam Filter server。SMTPserver可能需要同Spam Filter server通讯以便发现spam message或者RBL中列出的客户端。如果这两个server是在同一个java虚拟机中,一个IO层是多余的,你可以绕过message对象的编解码的过程。InVMpipe communication可以使你使用同样的代码而不管spam filter server是否在同一个虚拟机中。 请查看随源码分发的’ Tennis’示例。

原创粉丝点击