从I/O多路复用到Netty,还要跨过Java NIO包(一)

简介: 从I/O多路复用到Netty,还要跨过Java NIO包(一)

1.先来看一个Java NIO服务端的例子


上一篇文章我们已经了解了I/O多路复用的实现形式。
就是多个的进程的IO可以注册到一个复用器(selector)上,然后用一个进程调用select,select会监听所有注册进来的IO。

NIO包做了对应的实现。如下图所示。

64.png


有一个统一的selector负责监听所有的Channel。这些channel中只要有一个有IO动作,就可以通过Selector.select()方法检测到,并且使用selectedKeys得到这些有IO的channel,然后对它们调用相应的IO操作。


我们来个简单的demo做一下演示。如何使用NIO中三个核心组件(Buffer缓冲区、Channel通道、Selector选择器)来编写一个服务端程序。


public class NioDemo {
    public static void main(String[] args) {
        try {
            //1.创建channel
            ServerSocketChannel socketChannel1 = ServerSocketChannel.open();
            //设置为非阻塞模式,默认是阻塞的
            socketChannel1.configureBlocking(false);
            socketChannel1.socket().bind(new InetSocketAddress("127.0.0.1", 8811));
            ServerSocketChannel socketChannel2 = ServerSocketChannel.open();
            socketChannel2.configureBlocking(false);
            socketChannel2.socket().bind(new InetSocketAddress("127.0.0.1", 8822));
            //2.创建selector,并将channel1和channel2进行注册。
            Selector selector = Selector.open();
            socketChannel1.register(selector, SelectionKey.OP_ACCEPT);
            socketChannel2.register(selector, SelectionKey.OP_ACCEPT);
            while (true) {
                //3.一直阻塞直到有至少有一个通道准备就绪
                int readChannelCount = selector.select();
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                //4.轮训已经就绪的通道
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    iterator.remove();
                    //5.判断准备就绪的事件类型,并作相应处理
                    if (key.isAcceptable()) {
                        // 创建新的连接,并且把连接注册到selector上,并且声明这个channel只对读操作感兴趣。
                        ServerSocketChannel serverSocketChannel = (ServerSocketChannel)key.channel();
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector, SelectionKey.OP_READ);
                    }
                    if (key.isReadable()) {
                        SocketChannel socketChannel = (SocketChannel) key.channel();
                        ByteBuffer readBuff = ByteBuffer.allocate(1024);
                        socketChannel.read(readBuff);
                        readBuff.flip();
                        System.out.println("received : " + new String(readBuff.array()));
                        socketChannel.close();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


通过这个代码示例,我们能清楚地了解如何用Java NIO包实现一个服务端:


  • 1)创建channel1和channel2,分别监听特定端口。
  • 2)创建selector,并将channel1和channel2进行注册。
  • 3)selector.select()一直阻塞,直到有至少有一个通道准备就绪。
  • 4)轮训已经就绪的通道
  • 5)并根据事件类型做出相应的响应动作。


程序启动后,会一直阻塞在selector.select()。
通过浏览器调用localhost:8811 或者 localhost:8822就能触发我们的服务端代码了。


2.Java NIO包如何实现I/O多路复用模型


上文演示的Java NIO服务端已经比较清楚地展示了使用NIO编写服务端程序的过程。


那这个过程中如何实现了I/O多路复用的呢?


我们得深入看下selector的实现。


//2.创建selector,并将channel1和channel2进行注册。
Selector selector = Selector.open();


从open这里开始吧。

65.png


这里用了一个SelectorProvider来创建selector。


进入SelectorProvider.provider(),看到具体的provider是由
sun.nio.ch.DefaultSelectorProvider创建的,对应的方法是:

66.png


咦?原来不同的操作系统会提供不同的provider对象。这里包括了PollSelectorProvider、EPollSelectorProvide等。


名字是不是有点眼熟?


没错,跟我们上一篇文章分析过的I/O多路复用的不同实现方式poll/epoll有关。


我们选择默认的


sun.nio.ch.PollSelectorProvider往下看看。

67.png


OK,找到了实现类PollSelectorImpl。


然后,通过以下调用:

68.png


找到最终的native方法poll0。

69.png


是不是仍然很眼熟?


没错!跟我们上一篇文章分析过的poll函数是一致的。


int poll (struct pollfd *fds, unsigned int nfds, int timeout);


绕了这么久,到最后,还是找到了我们聊过I/O多路复用的 poll 实现。


至此,我们终于把Java NIO和 I/O多路复用模型串联起来了。


Java NIO包使用selector,实现了I/O多路复用模型。


同时,在不同的操作系统中,会有不同的poll/epoll选择。



目录
相关文章
|
10月前
|
消息中间件 缓存 网络协议
Netty基础—4.NIO的使用简介
本文详细介绍了Java NIO(New Input/Output)的核心概念与编程模型。首先,讲解了Buffer缓冲区的作用及4个核心概念:capacity、limit、position、mark,并通过Direct模式创建的Buffer示例展示了其高性能特点。接着,分析了Channel通道的概念,说明其与Buffer的关系以及FileChannel在文件读写中的应用,包括顺序写、随机写和多线程安全特性。 随后,对比了BIO(Blocking IO)编程模型的局限性,如线程资源耗尽问题,引出伪异步IO编程的改进方案,但指出其仍存在级联故障风险。进一步探讨了长连接与短连接的区别及其实现代码。
|
11月前
|
消息中间件 算法 安全
JUC并发—1.Java集合包底层源码剖析
本文主要对JDK中的集合包源码进行了剖析。
|
前端开发 JavaScript Java
[Java计算机毕设]基于ssm的OA办公管理系统的设计与实现,附源码+数据库+论文+开题,包安装调试
OA办公管理系统是一款基于Java和SSM框架开发的B/S架构应用,适用于Windows系统。项目包含管理员、项目管理人员和普通用户三种角色,分别负责系统管理、请假审批、图书借阅等日常办公事务。系统使用Vue、HTML、JavaScript、CSS和LayUI构建前端,后端采用SSM框架,数据库为MySQL,共24张表。提供完整演示视频和详细文档截图,支持远程安装调试,确保顺利运行。
479 17
|
缓存 网络协议 Java
JAVA网络IO之NIO/BIO
本文介绍了Java网络编程的基础与历史演进,重点阐述了IO和Socket的概念。Java的IO分为设备和接口两部分,通过流、字节、字符等方式实现与外部的交互。
440 0
|
监控 Java API
探索Java NIO:究竟在哪些领域能大显身手?揭秘原理、应用场景与官方示例代码
Java NIO(New IO)自Java SE 1.4引入,提供比传统IO更高效、灵活的操作,支持非阻塞IO和选择器特性,适用于高并发、高吞吐量场景。NIO的核心概念包括通道(Channel)、缓冲区(Buffer)和选择器(Selector),能实现多路复用和异步操作。其应用场景涵盖网络通信、文件操作、进程间通信及数据库操作等。NIO的优势在于提高并发性和性能,简化编程;但学习成本较高,且与传统IO存在不兼容性。尽管如此,NIO在构建高性能框架如Netty、Mina和Jetty中仍广泛应用。
491 3
|
存储 监控 Java
Java的NIO体系
通过本文的介绍,希望您能够深入理解Java NIO体系的核心组件、工作原理及其在高性能应用中的实际应用,并能够在实际开发中灵活运用这些知识,构建高效的Java应用程序。
467 5
|
Java Android开发
Eclipse 创建 Java 包
Eclipse 创建 Java 包
245 1
|
消息中间件 缓存 Java
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
零拷贝技术 Zero-Copy 是指计算机执行操作时,可以直接从源(如文件或网络套接字)将数据传输到目标缓冲区, 而不需要 CPU 先将数据从某处内存复制到另一个特定区域,从而减少上下文切换以及 CPU 的拷贝时间。
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
|
Java 数据库连接 数据库
深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能
在Java应用开发中,数据库操作常成为性能瓶颈。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能。文章介绍了连接池的优势、选择和使用方法,以及优化配置的技巧。
279 1
|
SQL Java 数据库连接
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率。本文介绍了连接池的工作原理、优势及实现方法,并提供了HikariCP的示例代码。
301 3