`

JavaNIO与Reactor模式

    博客分类:
  • java
 
阅读更多

一、NIO的简单介绍 

      Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从Java 1.4开始),Java NIO提供了与标准IO不同的IO工作方式。 NIO中的核心内容有Channel、Buffer、Selector,其他组件如Pipe和FileLock只不过是三个组件共同使用的工具类。

     1、Channle

          Channle的实现主要有FileChannel、DatagramChannel、SocketChannle、ServerSocketChannel。这些通道覆盖了文件IO、UDP和TCP网络IO

     2、Buffer

          Buffer的实现由ByteBuf、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer以及MappedByteBuffer(用于表示内存映射文件)。以上Buffer涵盖了能够通过IO发送的基本数据类型。

     3、Selector

       Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件。这样,一个单独的线程可以管理多个channel,从而管理多个网络连接。

   二、NIO在网络服务端的应用。

      2.1、传统ServerSocket

       当我们刚开始学习想要做一个聊天服务器的时候,最简单的做法就是使用ServerSocket来作为服务端,接收Socket客户端发送来的数据。下面的代码就是一个简单的 例子,当我们从服务端接收到数据时,新建一个线程来处理我们的数据,这种模式是一种阻塞式IO,在高并发的情况下,我们需要新建大量线程,然而程序的效率在线程数不多的情况下是随着线程的增加而增加,但是到达一定数量后是随着线程的增加而减少。所以传统阻塞式IO的瓶颈在于不能处理过多的连接。

    2.2、NIO非阻塞式服务。

       javaNIO中的ServerSocketChannel是一个可以监听新进来的TCP连接的通道,就像标准的ServerSocket一样。我们在这里使用NIO来搭建一个服务端。

    

package NIO;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

public class NIOServer {

    public static  void main(String[] args)throws IOException{
        //创建一个NIOsocket服务
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //将ServerSocketChannel设置为非阻塞
        serverSocketChannel.configureBlocking(false);
        //serverSocketChannel.bind(new InetSocketAddress(8989));
        serverSocketChannel.socket().bind(new InetSocketAddress(8989));
        //调用 Selector.open()方法创建一个selector
        Selector selector = Selector.open();
        //将serverSocketChannel注册到selector中,监听TCP连接
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        while(true){
            //这条语句会阻塞
            int nkey = selector.select();
           //也可以设置超时时间,防止进程阻塞
          //int nkey = selector.select(long timeout);
            System.out.println("nkey"+nkey);
            if(nkey>0) {
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                int i = 0;
                while (iterator.hasNext()) {
                    i++;
                    System.out.println("iteratorLength" + i);
                    SelectionKey key = iterator.next();
                    iterator.remove();
                    reactor(key, selector);
                }
            }
        }
    }

    public static void reactor(SelectionKey key,Selector selector) throws IOException {
        if (key.isAcceptable()) {
            //接收就绪状态
            handlerAccept(key,selector);
        } else if (key.isReadable()) {
            //读就绪状态
            handlerReader(key);
        }
    }

    public static void handlerAccept(SelectionKey key,Selector selector) throws IOException {
        ServerSocketChannel sever = (ServerSocketChannel) key.channel();
        SocketChannel channel = sever.accept();
        channel.configureBlocking(false);
        System.out.println("客户端连接" + channel.toString());
        //将通道注册到selector中
        //此时Selector监听channel并对read感兴趣
        channel.register(selector, SelectionKey.OP_READ);
    }

    public  static void handlerReader(SelectionKey key) throws IOException {

        SocketChannel socketChannel = (SocketChannel) key.channel();
        //读取部分消息,不确定数据长度只用了1024个字节长度接收,当数据大于1024时,会丢失数据,当数据只有几十个字节时会浪费内存空间,当并发数据量大时会造成不必要的浪费
        // 文章 http://ifeve.com/non-blocking-server/
        //  http://tutorials.jenkov.com/java-performance/resizable-array.html
        // 中介绍了如何实现这样支持可调整大小的数组的内存缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //不会阻塞
        int n =0;

        //此处需要捕获异常,否则若客户端强制关闭,服务器会报“Java.io.IOException: 远程主机强迫关闭了一个现有的连接。”,
        // 并且服务器会在报错后停止运行,错误的意思就是客户端关闭了,但是服务器还在从这个套接字通道读取数据,便抛出IOException,
        // 导致这种情况出现的原因就是,客户端异常关闭后,服务器的选择器会获取到与客户端套接字对应的套接字通道SelectionKey,
        // 并且这个key的兴趣是OP_READ,执行从这个通道读取数据时,客户端已套接字已关闭,所以会出现“java.io.IOException: 远程主机强迫关闭了一个现有的连接”的错误。
        // 所以在服务器在读取数据时,若发生异常,则取消当前key并关闭通道,如下代码:
        try {
            n = socketChannel.read(buffer);
        }catch (Exception e){
            e.printStackTrace();
            key.cancel();
            socketChannel.socket().close();
            socketChannel.close();
            return;
        }
        System.out.println("读取字节数"+n);
        if (n > 0) {
            byte[] data = buffer.array();
            System.out.println("服务端收到信息:" + new String(data, 0, n));
            buffer.flip();
            socketChannel.write(buffer);
        } else {
            System.out.println("clinet is close");
            key.cancel();
        }

    }
}

       上面是一个非阻塞式IO的服务端。其设计是一个经典的单线程的reactor模式。

三、Reactor模式

 

 

     Reactor不仅监控所有的连接请求,还监控其他读请求。我们将多个channle注册到selector中,其中serverSocketChannle只注册了OP_ACCEPT事件,而socketChannel只注册了OP_READ事件。

       Select调用select()方法后,检测所有注册的通道,返回给Reactor(反应器)我们感兴趣的事件已经准备就绪的那些通道,然后Reactor分发给不同的handler处理(handlerAccept、handlerReader)。

 

 

  • 大小: 152.9 KB
分享到:
评论

相关推荐

    基于Java NIO反应器模式设计与实现

    Java NIO的反应器模式设计与实现,首先涉及到理解Java NIO的基本概念,如通道(Channel)、缓冲区(Buffer)、选择器(Selector)等。通道是进行读写操作的基础,是连接I/O源和数据的端点。缓冲区是在NIO中用于数据...

    Java NIO与IO性能对比分析.pdf

    Java NIO的Reactor模式是实现高并发的关键。Reactor模式是一种事件驱动的架构模式,它具有事件分离器、事件处理器以及复用的事件队列。事件分离器负责监听事件的发生,事件处理器对相应的事件做出响应。在Java NIO中...

    Reactor模式和NIO

    Reactor模式是一种事件驱动的设计模式,主要用于解决高并发场景下的系统设计问题,而Java的NIO(Non-blocking Input/Output,非阻塞I/O)是Java平台提供的一种I/O模型,它支持基于事件的多路复用,为实现Reactor模式...

    java NIO socket聊天室

    可以作为NIO socket入门的例子,Reactor模式,重点理解key.attach, jar文件里包含了源代码 1,运行server.bat启动服务器,可以打开编辑,修改端口号 2,运行client.bat启动客户端,可以打开编辑,ip,和端口号 3...

    socket-nio-single-reactor.zip

    本示例代码旨在帮助开发者理解如何使用Java NIO和Reactor模式构建网络服务。尽管测试代码可能不完全准确,但它能展示基本的原理和工作流程。 首先,我们要了解什么是Reactor模式。Reactor模式是一种事件驱动的设计...

    Java NIO核心概念总结篇

    与传统的阻塞 I/O 相比,NIO 的设计思想更为先进,它采用了 Reactor 模式的事件驱动机制来实现非阻塞 I/O。通过这种方式,Java NIO 能够显著提升 I/O 性能,并且在某些场景下的性能甚至可以与 C 语言媲美。 **...

    JavaNIO的原理.pdf

    而Java NIO通过非阻塞模式和Reactor模式解决了这个问题。 Reactor模式是一种事件驱动的设计模式,用于处理多个并发连接。在Java NIO中,Selector(选择器)充当了Reactor的角色,它可以同时监视多个Channel(通道)...

    01_尚硅谷_Java NIO_课件_V1.01

    Java NIO 概述与实现详解 Java NIO(New IO 或 Non Blocking IO)是从 Java 1.4 版本开始引入的一个新的IO API,可以替代标准的 Java IO API。NIO 支持面向缓冲区的、基于通道的 IO 操作。NIO 将以更加高效的方式...

    java nio 原理浅析

    - **多级Reactor多线程模型**:主从Reactor模式的进一步扩展,增加了工作线程池,确保业务处理与I/O操作解耦,提高并发能力。 Netty的默认模式类似于第三种模型,但是没有使用线程池,而是由NioWorker类处理连接后...

    基于Java NIO的网络编程框架.zip

    本项目深入探讨了Java网络编程中的多种模式,包括BIO(阻塞IO)、NIO(非阻塞IO)、IO多路复用(select、poll、epoll)、Reactor模式,以及零拷贝技术。通过这些实现,项目展示了如何在高并发环境下优化网络通信效率...

    Java NIO原理和使用

    Java NIO非堵塞技术实际是采取Reactor模式,或者说是Observer模式为我们监察I/O端口,如果有内容进来,会自动通知我们,这样,我们就不必开启多个线程死等,从外界看,实现了流畅的I/O读写,不堵塞了。 Java NIO...

    Java NIO:浅析IO模型_动力节点Java学院整理

    Java NIO:浅析IO模型 ...Reactor模式是指在IO操作过程中,将所有的IO事件都交给一个事件处理器来处理。Proactor模式是指在IO操作过程中,将所有的IO事件都交给一个事件处理器来处理,然后将结果回调给应用程序。

    bio-nio-aio.zip

    总结,Java IO的发展经历了从BIO到NIO的转变,再到Reactor模式的优化,每一步都是为了提高系统的并发处理能力和资源利用率。理解并熟练掌握这些IO模型,对于开发高效、稳定的服务器端程序至关重要。通过对压缩包中的...

    Reactor Pattern (一)

    在Java中,NIO(Non-blocking I/O)框架就是Reactor模式的一个典型应用,它允许一个线程处理多个通道(Channel)上的I/O事件,而不是为每个连接创建一个单独的线程。NIO中的Selector组件就是Reactor的核心,它能够...

    ScalableIOJava(NIO如何实现Selector模式的).pdf

    标题《Scalable IO in Java》和描述表明本文档讨论...通过理解Reactor模式以及如何在Java中通过NIO API实现这一模式,开发者可以构建出能够处理大量并发连接的应用程序,并达到高可伸缩性、高可用性以及高性能的目标。

    基于Java NIO的网络服务器Netty生产实例.zip

    3. **Netty的事件驱动模型**:Netty采用 reactor 模式,通过EventLoopGroup来管理事件循环线程,每个线程负责处理多个连接的事件。当有新连接、读写事件发生时,会触发相应的ChannelHandler进行处理。 4. **Netty的...

    简单了解JAVA NIO

    NIO 通过 Reactor 模式的事件驱动机制来实现非阻塞式 I/O 操作。 Buffer Buffer 是 NIO 中的一个核心概念,实质上是一个容器对象。所有数据都需要经过 Buffer 对象来处理。Buffer 提供了对数据的结构化访问,并...

    javaNIO学习笔记(csdn)————程序.pdf

    **事件驱动的Reactor模式**: - **Synchronous Event Demultiplexer(同步事件分离器)**:如Java NIO的Selector,它监听多个通道并通知事件。 - **Initiation Dispatcher(初始分发器)**:Reactor角色,注册、删除...

    Java Nio实现React堆线程模型-netty首要知识

    通过学习这些材料,你可以深入理解如何利用Java Nio和Reactor模式构建高效、可扩展的网络服务,这对于理解Netty框架和开发高性能的Java网络应用至关重要。此外,掌握Reactor模式也有助于你设计和实现其他并发处理...

Global site tag (gtag.js) - Google Analytics