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



目录
相关文章
|
3天前
|
监控 Java API
探索Java NIO:究竟在哪些领域能大显身手?揭秘原理、应用场景与官方示例代码
Java NIO(New IO)自Java SE 1.4引入,提供比传统IO更高效、灵活的操作,支持非阻塞IO和选择器特性,适用于高并发、高吞吐量场景。NIO的核心概念包括通道(Channel)、缓冲区(Buffer)和选择器(Selector),能实现多路复用和异步操作。其应用场景涵盖网络通信、文件操作、进程间通信及数据库操作等。NIO的优势在于提高并发性和性能,简化编程;但学习成本较高,且与传统IO存在不兼容性。尽管如此,NIO在构建高性能框架如Netty、Mina和Jetty中仍广泛应用。
14 3
|
10天前
|
存储 监控 Java
Java的NIO体系
通过本文的介绍,希望您能够深入理解Java NIO体系的核心组件、工作原理及其在高性能应用中的实际应用,并能够在实际开发中灵活运用这些知识,构建高效的Java应用程序。
26 5
|
1月前
|
Java Android开发
Eclipse 创建 Java 包
Eclipse 创建 Java 包
33 1
|
1月前
|
消息中间件 缓存 Java
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
零拷贝技术 Zero-Copy 是指计算机执行操作时,可以直接从源(如文件或网络套接字)将数据传输到目标缓冲区, 而不需要 CPU 先将数据从某处内存复制到另一个特定区域,从而减少上下文切换以及 CPU 的拷贝时间。
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
|
2月前
|
Java Apache Maven
Java/Spring项目的包开头为什么是com?
本文介绍了 Maven 项目的初始结构,并详细解释了 Java 包命名惯例中的域名反转规则。通过域名反转(如 `com.example`),可以确保包名的唯一性,避免命名冲突,提高代码的可读性和逻辑分层。文章还讨论了域名反转的好处,包括避免命名冲突、全球唯一性、提高代码可读性和逻辑分层。最后,作者提出了一个关于包名的问题,引发读者思考。
Java/Spring项目的包开头为什么是com?
|
2月前
|
Java
让星星⭐月亮告诉你,Java NIO之Buffer详解 属性capacity/position/limit/mark 方法put(X)/get()/flip()/compact()/clear()
这段代码演示了Java NIO中`ByteBuffer`的基本操作,包括分配、写入、翻转、读取、压缩和清空缓冲区。通过示例展示了`position`、`limit`和`mark`属性的变化过程,帮助理解缓冲区的工作原理。
38 2
|
3月前
|
Java API 数据处理
Java 包(package)的作用详解
在 Java 中,包(package)用于组织和管理类与接口,具有多项关键作用:1)系统化组织代码,便于理解和维护;2)提供命名空间,避免类名冲突;3)支持访问控制,如 public、protected、默认和 private,增强封装性;4)提升代码可维护性,实现模块化开发;5)简化导入机制,使代码更简洁;6)促进模块化编程,提高代码重用率;7)管理第三方库,避免命名冲突;8)支持 API 设计,便于功能调用;9)配合自动化构建工具,优化项目管理;10)促进团队协作,明确模块归属。合理运用包能显著提升代码质量和开发效率。
224 4
|
存储 缓存 NoSQL
跟着源码学IM(十一):一套基于Netty的分布式高可用IM详细设计与实现(有源码)
本文将要分享的是如何从零实现一套基于Netty框架的分布式高可用IM系统,它将支持长连接网关管理、单聊、群聊、聊天记录查询、离线消息存储、消息推送、心跳、分布式唯一ID、红包、消息同步等功能,并且还支持集群部署。
13531 1
|
7月前
|
消息中间件 Oracle Dubbo
Netty 源码共读(一)如何阅读JDK下sun包的源码
Netty 源码共读(一)如何阅读JDK下sun包的源码
144 1
|
NoSQL Java Redis
跟着源码学IM(十二):基于Netty打造一款高性能的IM即时通讯程序
关于Netty网络框架的内容,前面已经讲了两个章节,但总归来说难以真正掌握,毕竟只是对其中一个个组件进行讲解,很难让诸位将其串起来形成一条线,所以本章中则会结合实战案例,对Netty进行更深层次的学习与掌握,实战案例也并不难,一个非常朴素的IM聊天程序。 原本打算做个多人斗地主练习程序,但那需要织入过多的业务逻辑,因此一方面会带来不必要的理解难度,让案例更为复杂化,另一方面代码量也会偏多,所以最终依旧选择实现基本的IM聊天程序,既简单,又能加深对Netty的理解。
176 1