Mina 的 API 当前主要有三个分支,分别是:
这里将要介绍的是 2.0.x 版。虽然当前的稳定版本还是 1.1.x ,但是按照 Mina 团队之前的开发计划, 2.0.x 即将在 08 年夏季正式发布,并且在 2.0.x 中对 Spring 等 IoC 的集成进行了简化,添加了基于 OGNL 的 JMX 远程管理支持,使用基于 Java Annotation 的全新 API 大大简化了状态机编程,新的基于 Apache APR 的基础 I/O 组件促进了进一步的效率提升(据官方评测, APR 的效率较之 Sun NIO 要高出约 10%)。由于这一系列的重大改进,使得 2.0.x 成为十分令人期待的一个版本,无论是 Mina 新手还是老用户,如果你对这个项目抱有兴趣,便很有必要提前对这个版本进行一些了解。
首先让我们对异步 I/O 做一些基本的了解。异步 I/O 模型大体上可以分为两种,反应式( Reactive )模型和前摄式( Proactive )模型:
传统的 select / epoll / kqueue 模型,以及 Java NIO 模型,都是典型的反应式模型,即应用代码对 I/O 描述符进行注册,然后等待 I/O 事件。当某个或某些 I/O 描述符所对应的 I/O 设备上产生 I/O 事件(可读、可写、异常等)时,系统将发出通知,于是应用便有机会进行 I/O 操作并避免阻塞。由于在反应式模型中应用代码需要根据相应的事件类型采取不同的动作,最常见的结构便是嵌套的 if {...} else {...} 或 switch ,并常常需要结合状态机来完成复杂的逻辑。
前摄式模型则恰恰相反。在前摄式模型中,应用代码主动地投递异步操作而不管 I/O 设备当前是否可读或可写。投递的异步 I/O 操作被系统接管,应用代码也并不阻塞在该操作上,而是指定一个回调函数并继续自己的应用逻辑。当该异步操作完成时,系统将发起通知并调用应用代码指定的回调函数。在前摄式模型中,程序逻辑由各个回调函数串联起来:异步操作 A 的回调发起异步操作 B ,B 的回调再发起异步操作 C ,以此往复。 Mina 便是一个前摄式的异步 I/O 框架。
前摄式模型相较于反射式模型往往更加难以编程。然而在具有原生异步 I/O 支持的操作系统中(例如支持 IO Completion Port 的 Win32 系统),采用前摄式模型往往可以取得比反应式模型更佳的效率。在没有原生异步 I/O 支持的系统中,也可以使用传统的反应式 API 对前摄式模型予以模拟。在现代的软硬件系统中,使用 epoll 和 kqueue 的前摄式模型实现同样可以轻松解决 C10K 问题。前摄式模型的一个显著优势是在实现复杂逻辑的时候不需要借助于状态机。因为状态机已经隐含在由回调串联起来的异步操作链当中了。如果上述内容难以理解,可以参考 Boost.Asio ,这是一个相当优秀的跨平台 C++ 前摄式 I/O 模型实现。
当然,对于程序员来说,还是直接看代码来得最为直接: Show me the code! 好,以下我们以官方文档上的一个简单的 TCP Time Server 为示例对 Mina 的基本服务器编程予以剖析。该服务器的功能是监听本地所有接口的 8150 端口,当有客户端连接建立时便向客户端以文本方式发送当前时间,并关闭连接。使用 Time Server 的目的在于
Time Server 源码分析
以下便是完整的服务端代码,稍后再逐行进行分析:
1 package test.mina.time.server;
2
3 import java.io.IOException;
4 import java.net.InetSocketAddress;
5 import java.util.Date;
6
7 import org.apache.commons.logging.Log;
8 import org.apache.commons.logging.LogFactory;
9 import org.apache.mina.common.IoAcceptor;
10 import org.apache.mina.common.IoHandlerAdapter;
11 import org.apache.mina.common.IoSession;
12 import org.apache.mina.filter.codec.ProtocolCodecFilter;
13 import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
14 import org.apache.mina.filter.logging.LoggingFilter;
15 import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
16
17 public class TimeServer {
18
19 static Log log = LogFactory.getLog( TimeServer.class );
20
21 public static void main( final String[] args ) {
22 final IoAcceptor acceptor = new NioSocketAcceptor();
23
24 acceptor.setHandler( new IoHandlerAdapter() {
25
26 @Override
27 public void messageSent( final IoSession session,
28 final Object message )
29 throws Exception {
30 session.close();
31 }
32
33 @Override
34 public void sessionOpened( final IoSession session )
35 throws Exception {
36 session.write( new Date() );
37 }
38
39 } );
40
41 acceptor.getFilterChain().addLast( "codec",
42 new ProtocolCodecFilter( new TextLineCodecFactory() ) );
43 acceptor.getFilterChain().addLast( "logging", new LoggingFilter() );
44
45 try {
46 acceptor.bind( new InetSocketAddress( 8150 ) );
47 }
48 catch( final IOException e ) {
49 log.error( "Bind error: ", e );
50 }
51 }
52
53 }
建立监听器
22 final IoAcceptor acceptor = new NioSocketAcceptor();
在传统服务端编程中,对于一个 TCP 服务器,我们需要先建立一个监听套接字。在 Mina 中,我们创建的并不是一个监听套接字,而是一个监听套接字工厂,或者称之为“监听器( acceptor )”。该概念映射到 Mina API 中,就是 IoAcceptor 接口及其各个实现类。
传统的 BSD Socket API 中的监听套接字以及其在 Java 中的对等物 java.net.ServerSocket 都是套接字工厂,其任务是在某个本地地址上进行监听,并在有客户端连接到来时产生一个与客户端进行通信的套接字。而 Mina 的 IoAcceptor 作为监听套接字的工厂可以接受一个包含多个本地地址的集合, IoAcceptor 会自行针对这个集合中的每个本地地址分别创建监听套接字并进行监听,并且在监听器销毁时进行适当的资源清理。这样便省去了我们建立自行维护多个监听套接字的麻烦。
监听套接字由 IoAcceptor 来接管,那么服务端接受客户端连接后产生的套接字又由谁接管呢?在 Mina 的术语中,一个 TCP 连接被称作一个“会话( session )”,对应的 Mina API 是 IoSession 接口。每当服务端接受一个客户端连接,便会创建出一个新的 IoSession 对象,通过该对象就可以对新建立的 TCP 连接进行各种操作。
在这个示例中,我们选用基于 Java NIO 的监听器实现 NioSocketAcceptor 。之后,每当服务器接受一个客户端连接, IoAccetpor 都会产生一个代表客户端和服务器 TCP 连接的 IoSession 对象。
设置事件回调
24 acceptor.setHandler( new IoHandlerAdapter() {
25
26 @Override
27 public void messageSent( final IoSession session,
28 final Object message )
29 throws Exception {
30 session.close();
31 }
32
33 @Override
34 public void sessionOpened( final IoSession session )
35 throws Exception {
36 session.write( new Date() );
37 }
38
39 } );
然后,我们要让这个 IoAcceptor 创建出的 IoSession 对象知道在各种事件发生时应该如何进行处理。这实际上就是文章开头所描述的反应式模型的应用层接口。只是反应式模型中的事件分支判断部分被 Mina 封装了起来,只暴露出包含了各种事件回调的 IoHandler 接口。可供处理的事件回调包括:
IoSession 对象被创建时的回调,一般用于进行会话初始化操作。注意,与 sessionOpened(IoSession) 回调不同, IoSession 对象的创建并不意味着对应的底层 TCP 连接的建立,而仅仅代表它的字面意思:一个 IoSession 对象被创建出来了。
IoSession 对象被打开时的回调。在 TCP 中,该事件是在 TCP 连接建立时触发的,一般可用于发起连接建立后的握手、认证等操作。
IoSession 对象被关闭时的回调。在 TCP 中,该事件是在 TCP 连接断开时触发的。一般可用于会话资源的清理等操作。
IoSession 对象超时时的回调。当一个 IoSession 对象在指定的超时时长内没有读写事件发生,就会触发该事件,一般可用于通知服务器断开长时间闲置的连接等处理。具体的超时设置可由 IoService.setWriteIdleTime(int) 、 IoService.setReadIdleTime(int) 和 IoService.setBothIdleTime(int) 设置。
当接收到 IoSession 对端发送的数据时的回调。
当发送给 IoSession 对端的数据发送成功时的回调。
当会话过程中出现异常时的回调。通常用于错误处理。
验证码:
注册会员 会员登陆