从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选择。



目录
相关文章
|
14天前
|
消息中间件 缓存 Java
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
零拷贝技术 Zero-Copy 是指计算机执行操作时,可以直接从源(如文件或网络套接字)将数据传输到目标缓冲区, 而不需要 CPU 先将数据从某处内存复制到另一个特定区域,从而减少上下文切换以及 CPU 的拷贝时间。
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
|
20天前
|
SQL Java 数据库连接
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率。本文介绍了连接池的工作原理、优势及实现方法,并提供了HikariCP的示例代码。
33 3
|
20天前
|
Java 数据库连接 数据库
深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能
在Java应用开发中,数据库操作常成为性能瓶颈。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能。文章介绍了连接池的优势、选择和使用方法,以及优化配置的技巧。
19 1
|
1月前
|
Java
让星星⭐月亮告诉你,Java NIO之Buffer详解 属性capacity/position/limit/mark 方法put(X)/get()/flip()/compact()/clear()
这段代码演示了Java NIO中`ByteBuffer`的基本操作,包括分配、写入、翻转、读取、压缩和清空缓冲区。通过示例展示了`position`、`limit`和`mark`属性的变化过程,帮助理解缓冲区的工作原理。
30 2
|
2月前
|
存储 网络协议 Java
Java NIO 开发
本文介绍了Java NIO(New IO)及其主要组件,包括Channel、Buffer和Selector,并对比了NIO与传统IO的优势。文章详细讲解了FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel及Pipe.SinkChannel和Pipe.SourceChannel等Channel实现类,并提供了示例代码。通过这些示例,读者可以了解如何使用不同类型的通道进行数据读写操作。
Java NIO 开发
|
3月前
|
网络协议 C# 开发者
WPF与Socket编程的完美邂逅:打造流畅网络通信体验——从客户端到服务器端,手把手教你实现基于Socket的实时数据交换
【8月更文挑战第31天】网络通信在现代应用中至关重要,Socket编程作为其实现基础,即便在主要用于桌面应用的Windows Presentation Foundation(WPF)中也发挥着重要作用。本文通过最佳实践,详细介绍如何在WPF应用中利用Socket实现网络通信,包括创建WPF项目、设计用户界面、实现Socket通信逻辑及搭建简单服务器端的全过程。具体步骤涵盖从UI设计到前后端交互的各个环节,并附有详尽示例代码,助力WPF开发者掌握这一关键技术,拓展应用程序的功能与实用性。
115 0
|
4月前
|
Java 大数据
解析Java中的NIO与传统IO的区别与应用
解析Java中的NIO与传统IO的区别与应用
|
4月前
|
Java
Java中的NIO编程详解
Java中的NIO编程详解
|
4月前
|
Java 大数据
如何在Java中进行网络编程:Socket与NIO
如何在Java中进行网络编程:Socket与NIO
|
4月前
|
Java
Java中的NIO编程详解
Java中的NIO编程详解