深入拆解Tomcat & Jetty
李号双
eBay技术主管
立即订阅
6067 人已学习
课程目录
已完结 44 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | Java程序员如何快速成长?
免费
模块一 必备基础 (4讲)
01 | Web容器学习路径
02 | HTTP协议必知必会
03 | 你应该知道的Servlet规范和Servlet容器
04 | 实战:纯手工打造和运行一个Servlet
模块二 整体架构 (9讲)
05 | Tomcat系统架构(上): 连接器是如何设计的?
06 | Tomcat系统架构(下):聊聊多层容器的设计
07 | Tomcat如何实现一键式启停?
08 | Tomcat的“高层们”都负责做什么?
09 | 比较:Jetty架构特点之Connector组件
10 | 比较:Jetty架构特点之Handler组件
11 | 总结:从Tomcat和Jetty中提炼组件化设计规范
12 | 实战:优化并提高Tomcat启动速度
13 | 热点问题答疑(1):如何学习源码?
模块三 连接器 (9讲)
14 | NioEndpoint组件:Tomcat如何实现非阻塞I/O?
15 | Nio2Endpoint组件:Tomcat如何实现异步I/O?
16 | AprEndpoint组件:Tomcat APR提高I/O性能的秘密
17 | Executor组件:Tomcat如何扩展Java线程池?
18 | 新特性:Tomcat如何支持WebSocket?
19 | 比较:Jetty的线程策略EatWhatYouKill
20 | 总结:Tomcat和Jetty中的对象池技术
21 | 总结:Tomcat和Jetty的高性能、高并发之道
22 | 热点问题答疑(2):内核如何阻塞与唤醒进程?
模块四 容器 (8讲)
23 | Host容器:Tomcat如何实现热部署和热加载?
24 | Context容器(上):Tomcat如何打破双亲委托机制?
25 | Context容器(中):Tomcat如何隔离Web应用?
26 | Context容器(下):Tomcat如何实现Servlet规范?
27 | 新特性:Tomcat如何支持异步Servlet?
28 | 新特性:Spring Boot如何使用内嵌式的Tomcat和Jetty?
29 | 比较:Jetty如何实现具有上下文信息的责任链?
30 | 热点问题答疑(3):Spring框架中的设计模式
模块五 通用组件 (4讲)
31 | Logger组件:Tomcat的日志框架及实战
32 | Manager组件:Tomcat的Session管理机制解析
33 | Cluster组件:Tomcat的集群通信原理
特别放送 | 如何持续保持对学习的兴趣?
模块六 性能优化 (8讲)
34 | JVM GC原理及调优的基本思路
35 | 如何监控Tomcat的性能?
36 | Tomcat I/O和线程池的并发调优
37 | Tomcat内存溢出的原因分析及调优
38 | Tomcat拒绝连接原因分析及网络优化
39 | Tomcat进程占用CPU过高怎么办?
40 | 谈谈Jetty性能调优的思路
41 | 热点问题答疑(4): Tomcat和Jetty有哪些不同?
结束语 (1讲)
结束语 | 静下心来,品味经典
深入拆解Tomcat & Jetty
登录|注册

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

李号双 2019-06-13
我在专栏上一期里提到了 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/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《深入拆解Tomcat & Jetty 》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(30)

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

    作者回复: 对的

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

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

    2019-06-13
    4
  • 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);

    ...
    }

    2019-06-15
    3
  • 西兹兹
    李老师好,请问nio1,tomcat里nio为什么不参考netty,通过使用堆外内存来避免零拷贝问题?

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

    2019-06-15
    3
  • 佑儿
    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);

    ...
    }
    以上是针对802.11的回答,可是我没有在Nio2EndPoint中找到对应poller内部类啊
     

    作者回复: 可能是代码版本不对,我看的是9.0.20最新版

    2019-06-25
    1
    2
  • -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没有使用这种方案。抱歉弄错了,还是不能人云亦云,一定要自己看了源码才行...



    2019-06-18
    2
  • Monday
    走到io就渐渐要掉队,虽然看了很多遍
    2019-06-17
    2
  • 西兹兹
    李老师好,这节精彩,特别是两次read讲的用心。
    1 nio2图里确实有个poller字样
    2 思考题回答,个人认为第二次read用的是work线程池, 因为内核已经准备好完整开箱可用的数据, 直接使用即可, 无须过多的线程上下文切换。
    2019-06-15
    2
  • W.T
    李老师实力派!👍
    2019-06-13
    2
  • WL
    老师关于IO模型内存我有两个问题:
    1. 配内存的时候,是不是因为堆内存会受到GC的影响导致地址变化,所以不能直接使用不能使用堆内存, 如果使用堆内存的话也需要先指向一个固定的堆外内存,所以使用堆外内存就可以避免GC对内存地址的影响。
    2. 是不是IO在读数据的时候经过两次数据拷贝,从网卡到内核态,从内核态到用户态,这两次数据拷贝有什么方法可以减少1次吗?

    作者回复: 好问题,下一篇就会解释这个问题。

    2019-06-13
    2
  • 世纪猛男
    "同时为了提高处理速度,会提供一个线程池给内核使用,样不会耽误内核线程的工作"

    请问老师,这里不太明白,用户线程不能操作内核空间, 那这个线程池 如何能替代 内核线程的工作
    2019-06-16
    1
  • nimil
    问下老师,这个Tomcat这个IO模型是将数据拷贝了两次么,还是有做特殊优化

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

    2019-06-13
    1
  • 帽子丨影
    老师好,文中说到 Http11Processor在处理请求时,会调用Nio2SocketWrapper的Read()。这个逻辑不知道在源码的什么位置,一直没找到
    2019-09-19
  • 不二
    老师,请教一下,在assc.accept(this, new AcceptHandler)方法注册的回调函数,在连接建立和数据到来内核是调用同一个回调函数处理的吗?

    2019-09-14
  • What for
    老师您好,有几个问题想请教下:
    1. 对于异步 IO 的理解,文中的 IO 过程其实注册的两个回调函数,一次用于建立连接一次用于读取数据,那么是不是 accept 和 read 是两个 IO 过程?两个回调函数都跑在 ChannelGroup 注册的线程池里?相当于 accept 和之后的两个回调函数一共用了 3 个线程?
    2. 异步 IO 中如果 read 函数的给定的 buffer 放不下一次连接传来的数据怎么办?
    3. 回调函数实现类的第二个类型参数本应是 attachment,但由于 NIO2Acceptor 本身就是回调函数它不需要显示指定 attachment 的类型参数,所以它的第二个类型参数为 Void?
    4. 关于思考题,如果我上面的理解没错的话,我觉得是不同的线程池,一个用于执行回调函数,另一个属于 Tomcat 用来执行 Handler 业务逻辑,不知道对不对...
    2019-08-24
  • 刘晓林
    老师,NIO2中,由于TCP是一个流,那内核把多少字节拷贝到buff中才触发回调函数呢?比如一次http请求,有没有可能触发回调函数时,还没有把一个完整的http请求包接受完整呀?

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

    2019-08-21
  • 星辰
    老师,我本来是想联系着看 Socket ServerSocket ServerSocketChannel AsynchronousServerSocketChannel 这些类之间的区别于联系,

    无奈看了一个就停下来有问题了, 我先把我看到的贴出来:

    public abstract class SocketImpl implements SocketOptions {
        /**
         * The actual Socket object.
         */
        Socket socket = null;
        ServerSocket serverSocket = null;

        /**
         * The file descriptor object for this socket.
         */
        protected FileDescriptor fd;

        /**
         * The IP address of the remote end of this socket.
         */
        protected InetAddress address;

        /**
         * The port number on the remote host to which this socket is connected.
         */
        protected int port;

        /**
         * The local port number to which this socket is connected.
         */
        protected int localport;
    }

    老师,原谅我是个差生。

    我晓得这是基础,但是很重要。

    我想去看看 java.net.Socket 与 java.net.ServerSocket的区别,

    发现 socket 和 serverSocket 在设计上 好像一点关系都有啊?

    都是通过 java.net.SocketImpl 来实现

    java.net.SocketImpl#bind

    java.net.SocketImpl#connect(java.net.InetAddress, int)

    java.net.SocketImpl#listen

    java.net.SocketImpl#accept

    java.net.SocketImpl#close

    这一系列的系统调用,是这样子嘛?

    另外,这两个 java.net.Socket 与 java.net.ServerSocket 都是在java.net包下,却是阻塞式的。是这样子嘛?

    恳请老师回答。

    作者回复: ServerSocket:用于在本机(Server端)开一个端口,被动的等待数据(用accept()方法),与 Client 端端建立连接后可以进行数据交换;

    Socket:用于连接远端机器(Server端)上的一个端口,主动发出数据,建立连接后也可以接收数据。

    2019-08-10
  • 星辰
    1. 需要再去看一下、熟悉一下asynchronousSocketChannel编程的范式

    2. 需要看今天专栏对应的源码,不懂了可以回来再看看专栏

    3. 当自己不懂的时候,都不知道如何提问。所以,应该是接受、理解知识,熟悉并记住它。见多了应该就好了。

    作者回复: 赞

    2019-08-10
  • brianway
    按照https://github.com/heroku/devcenter-embedded-tomcat嵌入式方式启动tomcat,是tomcat 8的,直接把依赖升级城9会启动失败。有现成的tomcat 9的嵌入式启动的demo工程吗
    2019-07-18
  • 802.11
    怎么模拟呢。现在springboot内置的Tomcat使用的都是nio 。设定NIO2呢

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

    2019-06-21
    1
收起评论
30
返回
顶部