MINA框架

MINA 基本类的描述

先认识几个接口:

IoAccepter 相当于网络应用程序中的服务器端

IoConnector 相当于客户端

IoSession 当前客户端到服务器端的一个连接实例

IoHandler 业务处理逻辑

IoFilter 过滤器用于悬接通讯层接口与业务层接口

 

MINA框架的常用类 
类NioSocketAcceptor用于创建服务端监听; 
类NioSocketConnector用于创建客户端连接;
 
类IoSession用来保存会话属性和发送消息;
 
类IoHandlerAdapter用于定义业务逻辑,常用的方法有:
 
方法定义
 
sessionCreated() 当会话创建时被触发
 
sessionOpened() 当会话开始时被触发
 
sessionClosed() 当会话关闭时被触发
 
sessionIdle() 当会话空闲时被触发
 
exceptionCaught() 当接口中其他方法抛出异常未被捕获时触发此方法
 
messageRecieved() 当接收到消息后被触发
 
messageSent() 当发送消息后被触发

MINA 的基础架构

下图是 MINA 的架构图,


图 1:MINA 的架构图

在 图中的模块链中,IoService 便是应用程序的入口,相当于我们前面代码中的IoAccepter,IoAccepter 便是 IoService 的一个扩展接口。IoService 接口可以用来添加多个IoFilter,这些 IoFilter 符合责任链模式并由 IoProcessor 线程负责调用。而 IoAccepter 在 ioService接口的基础上还提供绑定某个通讯端口以及取消绑定的接口。在上面的例子中,我们是这样使用IoAccepter 的:

IoAcceptor acceptor = new SocketAcceptor();

 

相当于我们使用了 Socket 通讯方式作为服务的接入,当前版本的 MINA 还提供了除SocketAccepter 外的基于数据报文通讯的 DatagramAccepter 以及基于管道通讯的VmPipeAccepter。另外还包括串口通讯接入方式,目前基于串口通讯的接入方式已经在最新测试版的 MINA 中提供。你也可以自行实现 IoService 接口来使用自己的通讯方式。

而在上图中最右端也就是 IoHandler,这便是业务处理模块。相当于前面例子中的 HelloHandler类。在业务处理类中不需要去关心实际的通讯细节,只管处理客户端传输过来的信息即可。编写Handler 类就是使用 MINA 开发网络应用程序的重心所在,相当于 MINA 已经帮你处理了所有的通讯方面的细节问题。为了简化 Handler 类,MINA 提供了 IoHandlerAdapter 类,此类仅仅是实现了IoHandler 接口,但并不做任何处理。

一个 IoHandler 接口中具有如下一些方法(摘自 MINA 的 API 文档):

void exceptionCaught(IoSession session, Throwable cause) 
当接口中其他方法抛出异常未被捕获时触发此方法

void messageReceived(IoSession session, Object message) 
当接收到客户端的请求信息后触发此方法.

void messageSent(IoSession session, Object message) 
当信息已经传送给客户端后触发此方法.

void sessionClosed(IoSession session) 
当连接被关闭时触发,例如客户端程序意外退出等等.

void sessionCreated(IoSession session) 
当一个新客户端连接后触发此方法.

void sessionIdle(IoSession session, IdleStatus status) 
当连接空闲时触发此方法.

void sessionOpened(IoSession session) 
当连接后打开时触发此方法,一般此方法与 sessionCreated 会被同时触发

 

前面我们提到 IoService 是负责底层通讯接入,而 IoHandler 是负责业务处理的。那么 MINA 架构图中的 IoFilter 作何用途呢?答案是你想作何用途都可以。但是有一个用途却是必须的,那就是作为 IoService 和 IoHandler 之间的桥梁。IoHandler 接口中最重要的一个方法是 messageReceived,这个方法的第二个参数是一个 Object 型的消息,总所周知,Object 是所有 Java 对象的基础,那到底谁来决定这个消息到底是什么类型呢?答案也就在这个 IoFilter 中。在前面使用的例子中,我们添加了一个 IoFilter 是 new ProtocolCodecFilter(new TextLineCodecFactory()),这个过滤器的作用是将来自客户端输入的信息转换成一行行的文本后传递给 IoHandler,因此我们可以在 messageReceived 中直接将 msg 对象强制转换成 String 对象。

而 如果我们不提供任何过滤器的话,那么在 messageReceived 方法中的第二个参数类型就是一个 byte 的缓冲区,对应的类是 org.apache.mina.common.ByteBuffer。虽然你也可以将解析客户端信息放在 IoHandler 中来做,但这并不是推荐的做法,使原来清晰的模型又模糊起来,变得 IoHandler 不只是业务处理,还得充当协议解析的任务。

MINA自身带有一些常用的过滤器,例如LoggingFilter(日志记录)、BlackListFilter(黑名单过滤)、CompressionFilter(压缩)、SSLFilter(SSL加密)等。

 

 

 

实现echo协议的IoHandler 

IoHandler是业务处理逻辑,通常,应用需要继承IoHandlerAdapter并实现需要的方法:

例如:

public class EchoProtocolHandler extends IoHandlerAdapter{}

 

 

 

handler绑定到一个server端口上

例子:

private static final int PORT = 8080; 
 
     
public static void main( String[] args ) throws Exception
     
{
         SocketAcceptor acceptor = 
new SocketAcceptor();
         SocketAcceptorConfig defaultConfig = 
new SocketAcceptorConfig();
         defaultConfig.setReuseAddress(
true);
      
         
// Bind
         acceptor.bind(new InetSocketAddress(PORT), new EchoProtocolHandler(),                        defaultConfig);
 
         System.out.println( "Listening on port " + PORT );
     }

 

添加IoFilters

 

IoFilter提供了更加有力的方式来扩展MINA。它拦截所有的IO事件进行事件的预处理和后处理。你可以把它想象成ServletfiltersIoFilter能够实现以下几种目的:
 
   
事件日志
   * 
性能检测
   * 
数据转换(e.g. SSL support)
   * 
防火墙等等

 

我们的echo协议handler不对任何IO事件进行日志。我们可以通过添加一个filter来增加日志能力。MINA提供了IoLoggingFilter来进行日志。我们只要添加日志filterServiceRegistry即可。


 DefaultIoFilterChainBuilder chain = config.getFilterChain();
 addLogger(chain);
 
 
private static void addLogger( DefaultIoFilterChainBuilder chain ) throws Exception

{     

chain.addLast( "logger", new LoggingFilter() ); 

}

 

 

 

 

 

MINA也提供了一个SSLfilter,但它需要JDK1.5

DefaultIoFilterChainBuilder chain = config.getFilterChain();
 addLogger(chain);


 
private static void addSSLSupport( DefaultIoFilterChainBuilder chain )
     
throws Exception
 
{
     SSLFilter sslFilter =
         
new SSLFilter( BogusSSLContextFactory.getInstance( true ) );
     chain.addLast( "sslFilter", sslFilter );
     System.out.println( "SSL ON" );
 }

 

协议层实现反转Echo协议

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

使用协议层必须实现5个接口:ProtocolHandlerProtocolProviderProtocolCodecFactory,
ProtocolEncoder ProtocolDecoder

可能看上去有点麻烦,但是请注意ProtocolCodecFactory, ProtocolEncoder, ProtocolDecoder是可以完全复用的;ApacheASN1项目为MINA提供了ASN.1解码器,更通用的解码器如:XMLjava对象序列化和简单的文本将在MINA的下一个版本中提供。一旦你实现了一个灵活的解码器,你可以在未来的应用中复用它,即使你不打算复用你的解码器,MINA也提供了一个很简单的方法来实现复杂的协议。

我们添加一个反转’server,它用于反转它接到的所有文本,我们通过它来示范如何编写一个协议层。

 

ProtocolSession

ProtocolSessionIO层的IoSession同样继承自Session。就像前面提到的,你只需撰写面向POJOmessage而不是ByteBuffer的。ProtocolEncodermessage对象解释成ByteBuffers以便IO层能够将他们输出到socket

 

ProtocolHandler

ProtocolHandler类似于IO层的IoHandler.dataReaddataWritten方法被替换成messageReceivedmessageSent。这是因为ProtocolDecoder已经将IO层接收到的 ByteBuffers转换成了message对象。

package org.apache.mina.examples.reverser;
 
 
import org.apache.mina.common.IoHandler;
 
import org.apache.mina.common.IoHandlerAdapter;
 
import org.apache.mina.common.IoSession;
 
 
public class ReverseProtocolHandler extends IoHandlerAdapter
 
{
     
public void exceptionCaught( IoSession session, Throwable cause )
     
{
         
// Close connection when unexpected exception is caught.
         session.close();
     }
 
     
public void messageReceived( IoSession session, Object message )
     
{
         
// Reverse received string
         String str = message.toString();
         StringBuffer buf = 
new StringBuffer( str.length() );
         
forint i = str.length()  1; i >= 0; i )
         
{
             buf.append( str.charAt( i ) );
         }
 
         
// and write it back.
         session.write( buf.toString() );
     }
 }

 

ProtocolEncoder  ProtocolDecoder

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

import org.apache.mina.protocol.*;


 
/**
  * {
@link ProtocolProvider} implementation for reverser server protocol.
 */

 
public class ReverseProtocolProvider implements ProtocolProvider
 
{
     
// Protocol handler is usually a singleton.
     private static ProtocolHandler HANDLER =
 
new ReverseProtocolHandler();
 
     
// Codec factory is also usually a singleton.
     private static ProtocolCodecFactory CODEC_FACTORY =
 
new ProtocolCodecFactory()
     
{
         
public ProtocolEncoder newEncoder()
         
{
             
// Create a new encoder.
             return new TextLineEncoder();
         }
 
         
public ProtocolDecoder newDecoder()
         
{
             
// Create a new decoder.
             return new TextLineDecoder();
         }
     };
 
     
public ProtocolCodecFactory getCodecFactory()
     
{
         
return CODEC_FACTORY;
     }
 
     
public ProtocolHandler getHandler()
     
{
         
return HANDLER;
     }
 }

 

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

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

 
public class Main
 
{
     
private static final int PORT = 8080;
 
     
public static void main( String[] args ) throws Exception
     
{
         ServiceRegistry registry = 
new SimpleServiceRegistry();
 
         
// Bind
         Service service = new Service( "reverse", TransportType.SOCKET, PORT );
         registry.bind( service, 
new ReverseProtocolProvider() );
 
         System.out.println( "Listening on port " + PORT );
     }
 }

 

添加 ProtocolFilters

ProtocolFilter IO层的IoFilter类似:

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

private static void addLogger( ServiceRegistry registry )
     
{
         ProtocolAcceptor acceptor = registry.getProtocolAcceptor( TransportType.SOCKET );
         acceptor.getFilterChain().addLast( "logger", 
new ProtocolLoggingFilter() );
         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和基于Stringgetterputter
 
如果你使用MINA,你将不需要直接使用NIO buffers,因为仅使用MINA buffers就可以完成大多数buffer操作。
 
 ==== ByteBuffer 
 ====

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

MINA通过它灵活的filter机制来提供多种线程模型。没有线程池过滤器被使用时MINA运行在一个单线程模式。如果添加了一个IoThreadPoolFilterIoAcceptor,你将得到一个leaderfollower模式的线程池。如果再添加一个ProtocolThreadPoolFilter,你的server将有两个线程池;一个(IoThreadPoolFilter)被用于对message对象进行转换,另外一个(ProtocolThreadPoolFilter)被用于处理业务逻辑。

SimpleServiceRegistry加上IoThreadPoolFilterProtocolThreadPoolFilter的缺省实现即可适用于需要高伸缩性的应用。如果你想使用自己的线程模型,请查看SimpleServiceRegistry的源代码,并且自己初始化Acceptor。显然,这是个繁琐的工作。

IoThreadPoolFilter threadPool = new IoThreadPoolFilter();
 threadPool.start();
 
 IoAcceptor acceptor = 
new SocketAcceptor();
 acceptor.getFilterChain().addLast( "threadPool", threadPool );
 
 ProtocolThreadPoolFilter threadPool2 = 
new ProtocolThreadPoolFilter();
 threadPool2.start();
 
 ProtocolAcceptor acceptor2 = 
new IoProtocolAcceptor( 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 serverSMTP
server
可能需要同Spam Filter server通讯以便发现spam message或者RBL中列出的客户端。如果这两个server是在同一个java虚拟机中,一个IO层是多余的,你可以绕过message对象的编解码的过程。InVM pipe communication可以使你使用同样的代码而不管spam filter server是否在同一个虚拟机中。
 
请查看随源码分发的’ Tennis’示例。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值