Java NIO 在并发型服务器设计中的应用

cn7e

贡献于2013-10-18

字数:0 关键词: Java开发 Java

http://www.paper.edu.cn - 1 - Java NIO 在并发型服务器设计中的应用 丁辉 北京邮电大学 PCN&CAD 中心,北京( 100876) E-mail:lvsehaier@gmail.com 摘 要:本文分析了应用传统阻塞型网络 I/O 在进行服务器程序设计时的不足,在此基础上 研究了非阻塞型网络 I/O 的特性以及工作原理,给出了应用 NIO 工具包设计并发型服务器 程序的实现。 关键词: 阻塞,非阻塞, NIO, Java 1. 引言 随着多处理机体系结构的演变、网络技术的发展和分布式应用的兴起,并发型多任务的 服务器程序设计技术已越来越显示其重要性, 这要求服务器程序能够在几百个甚至更多的客 户端同时发出请求信息的情况下,仍能保持高性能的并发处理机制,迅速完成所有并发请求 的处理。传统的并发型服务器设计是利用阻塞型网络 I/O 以多线程的模式来实现的,然而由 于系统常常在进行网络读写时处于阻塞状态,会大大影响系统的性能;自 Java1. 4 开始引入 了 NIO(新 I/O) API,通过使用非阻塞型 I/O,实现流畅的网络读写操作,为开发高性能并发 型服务器程序提供了一个很好的解决方案。 2. 传统阻塞型网络 I/O 的不足 Java 平台传统的 I/O 系统都是基于 Byte(字节)和 Stream(数据流)的,相应的 I/O 操 作都是阻塞型的,所以服务器程序也采用阻塞型 I/O 进行数据的读、写操作。本文以 TCP 长连接模式来讨论并发型服务器的相关设计,为了实现服务器程序的并发性要求,系统由一 个单独的主线程来监听用户发起的连接请求, 一直处于阻塞状态; 当有用户连接请求到来时, 程序都会启一个新的线程来统一处理用户数据的读、写操作,其操作模式如图 1[1]所示。 图 1 基于阻塞 I/O 的多线程 TCP 服务器操作模式 这种模式的优点是简单、实用、易管理;然而缺点也是显而易见的:由于是为每一个客 户端分配一个线程来处理输入、输出数据,其线程与客户机的比例近似为 1:1,随着线程 数量的不断增加,服务器启动了大量的并发线程,会大大加大系统对线程的管理开销,这将 成为吞吐量瓶颈的主要原因;其次由于底层的 I/O 操作采用的同步模式, I/O 操作的阻塞管 http://www.paper.edu.cn - 2 - 理粒度是以服务于请求的线程为单位的 ,有可能大量的线程会闲置,处于盲等状态,造成 I/O 资源利用率不高,影响整个系统的性能。 对于并发型服务器,系统用在阻塞型 I/O 等待和线程间切换的时间远远多于 CPU 在内 存中处理数据的时间,因此传统的阻塞型 I/O 已经成为制约系统性能的瓶颈。 Java1.4 版本 后推出的 NIO 工具包,提供了非阻塞型 I/O 的异步输入输出机制,为提高系统的性能提供 了可实现的基础机制。 3. NIO 包及工作原理 针对传统 I/O 工作模式的不足, NIO 工具包提出了基于 Buffer(缓冲区)、 Channel(通 道)、Selector(选择器)的新模式 [2];Selector(选择器)、可选择的 Channel(通道)和 SelectionKey(选择键)配合起来使用,可以实现并发的非阻塞型 I/O 能力。 3.1 NIO 工具包的成员 [3] 3.1.1 Buffer(缓冲器) Buffer 类是一个抽象类,它有 7 个子类分别对应于七种基本的数据类型: ByteBuffer、 CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer 和 ShortBuffer。每一个 Buffer 对象相当于一个数据容器,可以把它看作内存中的一个大的数组,用来存储和提取所有基本 类型(boolean 型除外 )的数据。 Buffer 类的核心是一块内存区,可以直接对其执行与内存有关 的操作,利用操作系统特性和能力提高和改善 Java 传统 I/O 的性能。 3.1.2 Channel(通道) Channel 被认为是 NIO 工具包的一大创新点,是 (Buffer)缓冲器和 I/O 服务之间的通道, 具有双向性,既可以读入也可以写出,可以更高效的传递数据。我们这里主要讨论 ServerSocketChannel 和 SocketChannel,它们都继承了 SelectableChannel,是可选择的通道, 分别可以工作在同步和异步两种方式下(这里的可选择不是指可以选择两种工作方式,而是 指可以有选择的注册自己感兴趣的事件)。当通道工作在同步方式时,它的功能和编程方法 与传统的 ServerSocket、Socket 对象相似;当通道工作在异步工作方式时,进行输入输出处 理不必等到输入输出完毕才返回,并且可以将其感兴趣的(如:接受操作、连接操作、读出 操作、写入操作)事件注册到 Selector 对象上,与 Selector 对象协同工作可以更有效率的支 持和管理并发的网络套接字连接。 3.1.3 Selector(选择器)和 SelectionKey(选择键) 各类 Buffer 是数据的容器对象; 各类 Channel 实现在各类 Buffer 与各类 I/O 服务间传输 数据。Selector 是实现并发型非阻塞 I/O 的核心,各种可选择的通道将其感兴趣的事件注册 到 Selector 对象上, Selector 在一个循环中不断轮循监视这各些注册在其上的 Socket 通道。 SelectionKey 类则封装了 SelectableChannel 对象在 Selector 中的注册信息。当 Selector 监测 到在某个注册的 SelectableChannel 上发生了感兴趣的事件时 ,自动激活产生一个 SelectionKey 对象,在这个对象中记录了哪一个 SelectableChannel 上发生了哪种事件,通过对被激活的 SelectionKey 的分析,外界可以知道每个 SelectableChannel 发生的具体事件类型 ,进行相应的 处理。 http://www.paper.edu.cn - 3 - 3.2 NIO 工作原理 通过上面的讨论,我们可以看出在并发型服务器程序中使用 NIO,实际上是通过网络事 件驱动模型实现的。我们应用 Select 机制,不用为每一个客户端连接新启线程处理,而是将 其注册到特定的 Selector 对象上,这就可以在单线程中利用 Selector 对象管理大量并发的网 络连接,更好的利用了系统资源;采用非阻塞 I/O 的通信方式,不要求阻塞等待 I/O 操作完 成即可返回,从而减少了管理 I/O 连接导致的系统开销,大幅度提高了系统性能。NIO 的结 构如图 2 所示。 图 2 非阻塞网络 I/O 原理 从图中可以看出,当有读或写等任何注册的事件发生时,可以从 Selector 中获得相应的 SelectionKey ,从 SelectionKey 中可以找到发生的事件和该事件所发生的具体的 SelectableChannel,以获得客户端发送过来的数据。由于在非阻塞网络 I/O 中采用了事件触 发机制,处理程序可以得到系统的主动通知,从而可以实现底层网络 I/O 无阻塞、流畅地读 写,而不像在原来的阻塞模式下处理程序需要不断循环等待。使用 NIO,可以编写出性能更 好、更易扩展的并发型服务器程序。 4. 并发型服务器程序的实现代码 应用 NIO 工具包,基于非阻塞网络 I/O 设计的并发型服务器程序与以往基于阻塞 I/O 的 实现程序有很大不同,在使用非阻塞网络 I/O 的情况下,程序读取数据和写入数据的时机不 是由程序员控制的,而是 Selector 决定的。下面便给出基于非阻塞网络 I/O 的并发型服务器 程序的核心代码片段[4]: import java.io.*; //引入Java.io包 import java.net.*; //引入Java.net包 import java.nio.channels.*; //引入Java.nio.channels包 import java.util.*; //引入Java.util包 public class TestServer implements Runnable { /** http://www.paper.edu.cn - 4 - * 服务器Channel对象,负责接受用户连接 */ private ServerSocketChannel server; /** * Selector对象,负责监控所有的连接到服务器的网络事件的发生 */ private Selector selector; /** * 总的活动连接数 */ private int activeSockets; /** * 服务器Channel绑定的端口号 */ private int port ; /** * 构造函数 */ public TestServer()throws IOException { activeSockets=0; port=9999;//初始化服务器 Channel绑定的端口号为 9999 selector= Selector.open();//初始化Selector对象 server=ServerSocketChannel.open();//初始化服务器 Channel对象 ServerSocket socket=server.socket();//获取服务器 Channel对应的 //ServerSocket对象 socket.bind(new InetSocketAddress(port));//把Socket绑定到监听端口 9999上 server.configureBlocking(false);//将服务器 Channel设置为非阻塞模式 server.register(selector,SelectionKey.OP_ACCEPT);//将服务器 Channel注册到 Selector对象,并指出服务器 Channel所感兴趣的事件为可接受请求操作 } public void run() { while(true) { try { /** * 应用Select机制轮循是否有用户感兴趣的新的网络事件发生,当没有 http://www.paper.edu.cn - 5 - * 新的网络事件发生时,此方法会阻塞,直到有新的网络事件发生为止 */ selector.select(); } catch(IOException e) { continue;//当有异常发生时,继续进行循环操作 } /** * 得到活动的网络连接选择键的集合 */ Set keys=selector.selectedKeys(); activeSockets=keys.size();//获取活动连接的数目 if(activeSockets==0) { continue;//如果连接数为 0,则继续进行循环操作 } /** * 应用For—Each循环遍历整个选择键集合 */ for(SelectionKey key :keys) { /** * 如果关键字状态是为可接受,则接受连接,注册通道,以接受更多的 * 事件,进行相关的服务器程序处理 */ if(key.isAcceptable()) { doServerSocketEvent(key); continue; } /** * 如果关键字状态为可读,则说明 Channel是一个客户端的连接通道, * 进行相应的读取客户端数据的操作 */ if(key.isReadable()) { doClientReadEvent(key); continue; http://www.paper.edu.cn - 6 - } /** * 如果关键字状态为可写,则也说明 Channel是一个客户端的连接通道, * 进行相应的向客户端写数据的操作 */ if(key.isWritable()) { doClinetWriteEvent(key); continue; } } } } /** * 处理服务器事件操作 * @param key 服务器选择键对象 */ private void doServerSocketEvent(SelectionKey key) { SocketChannel client=null; try { ServerSocketChannel server=(ServerSocketChannel)key.channel(); client=server.accept(); if(client==null) { return; } client.configureBlocking(false);//将客户端 Channel设置为非阻塞型 /** * 将客户端Channel注册到 Selector对象上,并且指出客户端 Channel所感 * 兴趣的事件为可读和可写 */ client.register(selector,SelectionKey.OP_READ|SelectionKey.OP_READ); }catch(IOException e) { try { client.close(); http://www.paper.edu.cn - 7 - }catch(IOException e1){} } } /** * 进行向客户端写数据操作 * @param key 客户端选择键对象 */ private void doClinetWriteEvent(SelectionKey key) { 代码实现略; } /** * 进行读取客户短数据操作 * @param key 客户端选择键对象 */ private void doClientReadEvent(SelectionKey key) { 代码实现略; } } 从上面对代码可以看出,使用非阻塞性 I/O进行并发型服务器程序设计分三个部分: 1. 向Selector对象注册感兴趣的事件; 2.从Selector中获取所感兴趣的事件; 3.根据不同的事件进 行相应的处理。 5. 结语 通过使用 NIO 工具包进行并发型服务器程序设计,一个或者很少几个 Socket 线程就可 以处理成千上万个活动的 Socket 连接,大大降低了服务器端程序的开销;同时网络 I/O 采取 非阻塞模式,线程不再在读或写时阻塞,操作系统可以更流畅的读写数据并可以更有效地向 CPU 传递数据进行处理,以便更有效地提高系统的性能。 http://www.paper.edu.cn - 8 - 参考文献 [1] 程超,杨风召.《基于 Java 非阻塞 I/O 开发高性能网络应用程序》[J].电子工程师,2006,10:71-73 [2] 封玮,周世平.《基于 Java NIO 的非阻塞通信的研究与实现》[J].计算机系统应用,2004,9:32-35 [3] Ron Hitchens.《Java NIO》[M].O’Reilly.2002,8 [4] http://java.sun.com/j2se/1.5.0/docs/api/,J2SDK1.5.Sun Microsystems,2004 Application of Java NIO in Concurrent Server Program Design Ding Hui Beijing University of Posts and Telecommunications PCN&CAD Center Beijing (100876) Abstract This paper analyzes the deficiencies of server design applying traditional blocking network I/O. Based on this, this paper studied the feature and principle of non-blocking network I/O, a concurrent server implementation using NIO packages was given in the end. Keywords: blocking non_blocking NIO Java

下载文档,方便阅读与编辑

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 8 金币 [ 分享文档获得金币 ] 0 人已下载

下载文档

相关文档