深入拆解 Tomcat & Jetty
李号双
eBay 技术主管
38439 人已学习
新⼈⾸单¥68
登录后,你可以任选4讲全文学习
课程目录
已完结/共 45 讲
开篇词 (1讲)
深入拆解 Tomcat & Jetty
15
15
1.0x
00:00/00:00
登录|注册

15 | Nio2Endpoint组件:Tomcat如何实现异步I/O?

我在专栏上一期里提到了 5 种 I/O 模型,相应的,Java 提供了 BIO、NIO 和 NIO.2 这些 API 来实现这些 I/O 模型。BIO 是我们最熟悉的同步阻塞,NIO 是同步非阻塞,那 NIO.2 又是什么呢?NIO 已经足够好了,为什么还要 NIO.2 呢?
NIO 和 NIO.2 最大的区别是,一个是同步一个是异步。我在上期提到过,异步最大的特点是,应用程序不需要自己去触发数据从内核空间到用户空间的拷贝
为什么是应用程序去“触发”数据的拷贝,而不是直接从内核拷贝数据呢?这是因为应用程序是不能访问内核空间的,因此数据拷贝肯定是由内核来做,关键是谁来触发这个动作。
是内核主动将数据拷贝到用户空间并通知应用程序。还是等待应用程序通过 Selector 来查询,当数据就绪后,应用程序再发起一个 read 调用,这时内核再把数据从内核空间拷贝到用户空间。
需要注意的是,数据从内核空间拷贝到用户空间这段时间,应用程序还是阻塞的。所以你会看到异步的效率是高于同步的,因为异步模式下应用程序始终不会被阻塞。下面我以网络数据读取为例,来说明异步模式的工作过程。
首先,应用程序在调用 read API 的同时告诉内核两件事情:数据准备好了以后拷贝到哪个 Buffer,以及调用哪个回调函数去处理这些数据。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《深入拆解 Tomcat & Jetty 》
新⼈⾸单¥68
立即购买
登录 后留言

全部留言(44)

  • 最新
  • 精选
  • -W.LI-
    老师好!Windows 的 IOCP 和 Linux 内核 2.6 的 AIO 都提供了异步 I/O 的支持,Java 的 NIO.2 API 就是对操作系统异步 I/O API 的封装。 这句话怎么理解啊?我看别的老师说Linux不支持异步IO,评论里也有同学说Linux下的AIO其实只是NIO之类的。老师能具体讲讲么谢谢。

    作者回复: 刚我查了下JDK的源码,确实Java的NIO.2是通过epoll来模拟实现的,源码在这里: http://hg.openjdk.java.net/jdk9/dev/jdk/file/71716def08ac/src/java.base/linux/classes/sun/nio/ch/EPollPort.java Linux内核2.6确实提供了内核级的AIO支持,只是还不完善,详情可以看这里:http://lse.sourceforge.net/io/aio.html 估计是这个原因JDK没有使用这种方案。抱歉弄错了,还是不能人云亦云,一定要自己看了源码才行...

    3
    31
  • -W.LI-
    老师好!那个两次read能理解为,连接被保留着,数据没就绪处理的线程资源先释放了。收到异步数据就绪通知后,根据原有的连接重建处理线程,继续处理。阻塞期间线程可复用

    作者回复: 对的

    18
  • 王盛武
    李老师好,请问nio1,tomcat里nio为什么不参考netty,通过使用堆外内存来避免零拷贝问题?

    作者回复: 主要还是堆外内存管理起来没有JVM堆那么方便,为了稳定性的考虑吧,另外APR就是堆外内存的方案,也就是已经提供了这个选项。

    16
  • J.M.Liu
    老师,NIO2中,由于TCP是一个流,那内核把多少字节拷贝到buff中才触发回调函数呢?比如一次http请求,有没有可能触发回调函数时,还没有把一个完整的http请求包接受完整呀?

    作者回复: 对的,可能需要多次回调才能接收一个完整的请求

    2
    13
  • 发条橙子 。
    老师 , 这两张讲的I/O有点难啃 ,主要还是底子太薄 。 反复看了几遍有几个疑问点希望老师指点一下 😄 1. read请求是怎么发出来的 是通过调用select方法发出来的么 ? 2. 异步我看发了一个read 就返回了,那实际是应该是指异步非阻塞 , 那么存在异步阻塞的模型么? 3. 老师说从tcp/ip那层解数据包。我理解的过程是 当客户端发一个uri请求,当通过一系列的路由后最终到我们的服务器,再从七层网络模型的最底层开始一路向上到最顶层的应用层。 当应用层(Tomcat容器)接收到请求(连接器endpoint监听端口)后向操作系统发送一个read请求 ,然后等待操作系统内核回调应用程序(Tomcat容器)的回调接口 。那么按照我这种脑补的过程,实际上当服务端tomcat接收到客户端的I/O请求时,向操作系统发送read请求要求操作系统将客户端发送的数据(前台的入参信息)从内核拷贝到用户空间。因为tcp/ip层在应用层下面,那么从网卡解析数据到内核这个过程是不是在tomcat获取到请求之前的的时候就已经处理好了,而不是在tomcat发送read请求时再去从网卡解析数据..?

    作者回复: 1,select只是查询,真正发出read调用的还是read方法 2,好像没有异步阻塞这个说法 3,同步阻塞模型,read调用发起时,数据可能还没到网卡。如果io多路复用,read调用时,数据已经到了内核空间,因为之前select已经查到数据到了,应用才调read

    8
  • yungoo
    我所看的tomcat 8.5的代码跟专栏所讲已经有些不一致了。已经没有Nio2Acceptor了,accept获取连接用的是Future aceept()。

    作者回复: 我使用的是最新版的代码: https://github.com/apache/tomcat/blob/master/java/org/apache/tomcat/util/net/Nio2Endpoint.java#L382

    3
    8
  • nimil
    问下老师,这个Tomcat这个IO模型是将数据拷贝了两次么,还是有做特殊优化

    作者回复: 数据没有拷贝两次,第一次read调用是读不到数据的,因为这个时候数据还没应用层的Buffer,只是注册一个回调函数,当内核将数据拷贝到了应用层Buffer,调用回调函数,在回调函数里,HttpProccessor再发起一次read,read方法首先会检查数据是不是已经到了Buffer,如果是,直接读取Buffer返回,这一次并没有真正向内核发起read调用。

    7
  • 802.11
    怎么模拟呢。现在springboot内置的Tomcat使用的都是nio 。设定NIO2呢

    作者回复: 详细介绍 https://docs.spring.io/spring-boot/docs/current/reference/html/howto-embedded-web-servers.html

    3
    6
  • z.l
    linux没有真正实现异步IO,所以linux环境下NIO和NIO2的性能差别是不是不大 ?

    作者回复: 对的,NIO和NIO2其实差别不是很大,如果硬要区分,NIO适合处理比较轻的,数据传输量比较少的请求,AIO适合比较重,数据传输量比较大的请求。 这是因为NIO本质还是同步,数据从用户空间和内核空间之间的拷贝还是阻塞的。

    6
  • 802.11
    Http11Processor的2次read是在哪个类中呢,没有找到。。。。

    作者回复: Nio2SocketWrapper的read方法,这个方法会被调用两次,不是串行调两次,而是Poller会先后创建两个SocketProcessor任务类,在两个线程中执行,执行过程中每次Http11Processor都会调Nio2SocketWrapper的read方法。 public int read(boolean block, ByteBuffer to){ //第二次调用时直接通过这个方法取数据 int nRead = populateReadBuffer(to); ... //第一次时数据没取到,会调用下面这个方法去真正执行I/O操作并注册回调函数: nRead = fillReadBuffer(block); ... }

    6
收起评论
显示
设置
留言
44
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部