Java 性能调优实战
刘超
前金山软件技术经理
59174 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 49 讲
开篇词 (1讲)
模块一 · 概述 (2讲)
结束语 (1讲)
Java 性能调优实战
15
15
1.0x
00:00/00:00
登录|注册

11 | 答疑课堂:深入了解NIO的优化实现原理

epoll()函数
poll()函数
select()函数
使用fcntl
read、write阻塞
accept阻塞
connect阻塞
主从Reactor线程模型
多线程Reactor线程模型
单线程Reactor线程模型
Java的NIO编程中的Direct Buffer
Linux内核中的mmap函数
异步I/O
信号驱动式I/O
I/O复用
非阻塞式I/O
阻塞式I/O
性能对比测试
NIO线程模型
BIO线程模型
基于线程模型的Tomcat参数调优
线程模型优化
零拷贝
网络I/O模型优化
Tomcat中的I/O模型
Java NIO之深度解读

该思维导图由 AI 生成,仅供参考

你好,我是刘超。专栏上线已经有 20 多天的时间了,首先要感谢各位同学的积极留言,交流的过程使我也收获良好。
综合查看完近期的留言以后,我的第一篇答疑课堂就顺势诞生了。我将继续讲解 I/O 优化,对大家在 08 讲中提到的内容做重点补充,并延伸一些有关 I/O 的知识点,更多结合实际场景进行分享。话不多说,我们马上切入正题。
Tomcat 中经常被提到的一个调优就是修改线程的 I/O 模型。Tomcat 8.5 版本之前,默认情况下使用的是 BIO 线程模型,如果在高负载、高并发的场景下,可以通过设置 NIO 线程模型,来提高系统的网络通信性能。
我们可以通过一个性能对比测试来看看在高负载或高并发的情况下,BIO 和 NIO 通信性能(这里用页面请求模拟多 I/O 读写操作的请求):
测试结果:Tomcat 在 I/O 读写操作比较多的情况下,使用 NIO 线程模型有明显的优势。
Tomcat 中看似一个简单的配置,其中却包含了大量的优化升级知识点。下面我们就从底层的网络 I/O 模型优化出发,再到内存拷贝优化和线程模型优化,深入分析下 Tomcat、Netty 等通信框架是如何通过优化 I/O 来提高系统性能的。

网络 I/O 模型优化

网络通信中,最底层的就是内核中的网络 I/O 模型了。随着技术的发展,操作系统内核的网络模型衍生出了五种 I/O 模型,《UNIX 网络编程》一书将这五种 I/O 模型分为阻塞式 I/O、非阻塞式 I/O、I/O 复用、信号驱动式 I/O 和异步 I/O。每一种 I/O 模型的出现,都是基于前一种 I/O 模型的优化升级。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深度解读了Java NIO,重点讨论了Tomcat中的I/O优化和网络I/O模型优化。文章首先介绍了Tomcat中BIO和NIO线程模型的性能对比,指出在高负载或高并发情况下,NIO线程模型具有明显优势。接着从阻塞式I/O、非阻塞式I/O和I/O复用等角度深入探讨了网络I/O模型的优化。作者详细介绍了阻塞式I/O的工作流程和阻塞发生的环节,以及非阻塞式I/O和I/O复用的实现方式。其中,对于I/O复用,作者详细介绍了select/poll/epoll函数的使用方法和特点。最后,文章强调了epoll的性能优势,指出其采用事件驱动的方式代替轮询扫描fd,性能更胜一筹,并且不受fd数量限制。整体而言,本文通过深入的技术分析和实际案例,全面解读了Java NIO和网络I/O模型优化,对于想要深入了解I/O优化和网络通信性能提升的读者具有很高的参考价值。文章还介绍了信号驱动式I/O、异步I/O、零拷贝和线程模型优化等内容,为读者提供了全面的技术视角和实践经验。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Java 性能调优实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(42)

  • 最新
  • 精选
  • -W.LI-
    老师好!万分感觉,写的非常非常好谢谢。不过开心的同时,好多没看懂:-(先讲下我的理解吧。 阻塞IO:调用read()线程阻塞了 非阻塞IO:调用read()马上拿到一个数据未就绪,或者就绪。 I/O多路复用:selector线程阻塞,channel非阻塞,用阻塞一个selector线程换了多个channel了非阻塞。select()函数基于数组,fd个数限制1024,poll()函数也是基于数组但是fd数目无限制。都会负责所有的fd(未就绪的开销浪了), epll()基于红黑数实现,fd无大小限制,平衡二叉数插入删除效率高。 信号驱动模式IO:对IO多路复用进一步优化,selector也非阻塞了。但是sign信号无法区分多信号源。所以socket未使用这种,只有在单一信号模型上才能应用。 异步IO模型:真正的非阻塞IO,其实前面的四种IO都不是真正的非阻塞IO,他们的非阻塞只是,从网络或者内存磁盘到内核空间的非阻塞,调用read()后还需要从内核拷贝到用户空间。异步IO基于回调,这一步也非阻塞了,从内核拷贝到用户空间后才通知用户进程。 能我是这么理解的前半断,有理解错的请老师指正谢谢。后半断没看完。

    作者回复: 理解正确,赞一个

    2019-06-13
    27
  • -W.LI-
    老师好对Reacktor的三种模式还是理解不太好。帮忙看看哪里有问题 单线程模型:一个selector同时监听accept,事件和read事件。检测到就在一个线程处理。 多线程模型:一个线程监听accept事件,创建channel注册到selector上,检听到Read等事件从线程池中获取线程处理。 主从模式:没看懂:-(,一个端口只能被一个serverSocketChannel监听,第二个好像会报错?这边的主从怎么理解啊

    作者回复: 主从模式则是,Reactor主线程主要处理监听连接事件,而Reactor从线程主要监听I/O事件。这里是多线程处理accept事件,而不是创建多个ServerSocketChannel。

    2019-06-14
    3
    15
  • 每天晒白牙
    老师您在介绍Reactor线程模型的时候,关于多线程Reactor线程模型和主从Reactor线程模型,我有不同的理解。您画的多线程模型,其中读写交给了线程池,我在看Doug Lea的 《Scalable in java》中画的图和代码示例,读写事件还是由Reactor线程处理,只把业务处理交给了线程池。主从模型也是同样的,Reactor主线程处理连接,Reactor从线程池处理读写事件,业务交给单独的线程池处理。 还望老师指点

    作者回复: 你好,Reactor是一个模型,每个框架或者每个开发人员在处理I/O事件可能不一样,根据自己业务场景来处理。 Netty是基于Reactor主线程去监听连接, Reactor从线程池监听读写事件,同时如果监听到事件后直接在该从线程中操作读写I/O,将业务交给单独的业务线程池,也可以不交给单独的线程池处理,直接在从线程池处理。不交给业务线程池的好处是,减少上下文切换,坏处是会造成线程阻塞。 所以根据自己的业务的特性,如果你的数据特别大,I/O读写操作放到handler线程池,,Reactor从线程数量有限,如果开大了,由于开多个多路复用器也会带来性能消耗。所以这种处理也是一种提高系统吞吐量的优化。

    2019-06-15
    14
  • 你好旅行者
    I/O多路复用其实就相当于用了一个专门的线程来监听多个注册的事件,而之前的IO模型中,每一个事件都需要一个线程来监听,不知道我这样理解的是否正确?老师我还有一个问题,就是当select监听到一个事件到来时,它是另起一个线程把数据从内核态拷贝到用户态,还是自己就把这个事儿给干了?

    作者回复: 理解正确。select监听到事件之后就用当前线程把数据从内核态拷贝到用户态。

    2019-06-17
    2
    8
  • 行者
    感谢老师分享,联想到Redis的单线程模式,Redis使用同一个线程来做selector,以及处理handler,这样的优点是减少上下文切换,不需要考虑并发问题;但是缺点也很明显,在IO数据量大的情况下,会导致QPS下降;这是由Redis选择IO模型决定的。

    作者回复: 对的,redis本身是操作内存,所以读取数据的效率会高很多。

    2019-07-14
    6
  • z.l
    老师,隔壁李号双老师的《深入拆解Tomcat & Jetty》中关于DirectByteBuffer的解释和您不一样,他的文章中DirectByteBuffer的作用是:DirectByteBuffer 避免了 JVM 堆与本地内存直接的拷贝,而并没有避免内存从内核空间到用户空间的拷贝。而sendfile 特性才是避免了内核与应用之间的内存拷贝。请问哪种才是对的?

    作者回复: 这里的本地内存应该指的是物理内存,避免堆内存和物理内存的拷贝,其实就是避免内核空间和用户空间的拷贝。

    2019-06-16
    5
    6
  • 余冲
    老师能对reactor的几种模型,给一个简单版的代码例子看看吗。感觉通过代码应该能更好的理解理论。

    作者回复: 好的,后面补上

    2019-06-18
    2
    4
  • 阿卧
    老师,redis的io多路复用模型,用的是单线程reactor线程模型嘛?

    作者回复: 对的,redis在处理文件事件(例如GET SET命令)时是通过事件处理器循环顺序处理各个事件。

    2020-02-25
    3
  • chp
    老师,为什么说NIO是同步非阻塞呀?同步我知道原因,那个非阻塞搞不懂,select函数不是已经阻塞了吗,这块要怎么理解呢

    作者回复: 这里说的非阻塞,是伪非阻塞,操作系统层面的epoll还是阻塞的,正在实现操作系统层面的非阻塞是AIO

    2020-04-21
    2
  • insist
    感谢老师的讲解,很细致,从底层原理解释了5中IO模型。在netty,或者其他课程中,都有接触到这类知识,但是一直没有总结,总是看了感觉自己知道了,但是过段时间遇到这类问题,又不知道是为什么。

    作者回复: 大家都一样,有时间偶尔捡起来再看看,温故而知新,可以为师矣

    2020-01-04
    2
收起评论
显示
设置
留言
42
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部