深入拆解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
登录|注册

14 | NioEndpoint组件:Tomcat如何实现非阻塞I/O?

李号双 2019-06-11
UNIX 系统下的 I/O 模型有 5 种:同步阻塞 I/O、同步非阻塞 I/O、I/O 多路复用、信号驱动 I/O 和异步 I/O。这些名词我们好像都似曾相识,但这些 I/O 通信模型有什么区别?同步和阻塞似乎是一回事,到底有什么不同?等一下,在这之前你是不是应该问自己一个终极问题:什么是 I/O?为什么需要这些 I/O 模型?
所谓的 I/O 就是计算机内存与外部设备之间拷贝数据的过程。我们知道 CPU 访问内存的速度远远高于外部设备,因此 CPU 是先把外部设备的数据读到内存里,然后再进行处理。请考虑一下这个场景,当你的程序通过 CPU 向外部设备发出一个读指令时,数据从外部设备拷贝到内存往往需要一段时间,这个时候 CPU 没事干了,你的程序是主动把 CPU 让给别人?还是让 CPU 不停地查:数据到了吗,数据到了吗……
这就是 I/O 模型要解决的问题。今天我会先说说各种 I/O 模型的区别,然后重点分析 Tomcat 的 NioEndpoint 组件是如何实现非阻塞 I/O 模型的。

Java I/O 模型

对于一个网络 I/O 通信过程,比如网络数据读取,会涉及两个对象,一个是调用这个 I/O 操作的用户线程,另外一个就是操作系统内核。一个进程的地址空间分为用户空间和内核空间,用户线程不能直接访问内核空间。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《深入拆解Tomcat & Jetty 》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(83)

  • QQ怪
    当老师谈到阻塞和非阻塞及异步和同步概念的时候,我对比了很多其他网上的解释,发现我不太理解他们的,但唯独老师总结的真的很很透彻、很到位!!!崇拜老师,发现这个专栏真的很棒!!!
    2019-06-12
    1
    32
  • 🐛
    老师,操作系统级的连接指的是什么啊?

    作者回复: TCP三次握手建立连接的过程中,内核通常会为每一个LISTEN状态的Socket维护两个队列:

    SYN队列(半连接队列):这些连接已经接到客户端SYN;
    ACCEPT队列(全连接队列):这些连接已经接到客户端的ACK,完成了三次握手,等待被accept系统调用取走。


    Acceptor负责从ACCEPT队列中取出连接,当Acceptor处理不过来时,连接就堆积在ACCEPT队列中,这个队列长度也可以通过参数设置。

    2019-06-11
    13
  • Geek_28b75e
    问一个基础问题,线程的同步,和本节所讲的同步,意义上的不同

    作者回复: 不是一个概念,线程的同步一般指对共享变量的互斥访问。IO模型的同步是指应用和内核的交互方式。

    2019-06-11
    9
  • 🐟🐙🐬🐆🦌🦍🐑�...
    Io多路复用实际上也是同步非阻塞模式,用户线程阻塞在selector方法上,不像其他Io阻塞在read write 方法调用 .同步是指当内核准备好数据时,还是应用程序线程把内核数据同步到用户空间.
    2019-06-11
    7
  • Monday
    阻塞与同异步的区别
    本节的总结有如下的2句话,1)阻塞与非阻塞指的是应用程序发起i/o操作后是等待还是立即返回。2)同步与异步指的是应用程序在与内核通信时,数据从内核空间到应用空间的拷贝内核主动发起还是应用程序触发。
    1,阻塞对应的是等待,非阻塞对应的是立即返回。这句应该好理解。
    2,同步对应的是哪个?
    3,我的理解是js中ajax请求的有个属性,async为true异步false同步。这个对应了网络IO。好理解
    4,我的理解阻塞非阻塞是java的jcu包下ArrayBlockingQueue队列中的offer和put方法的区别。其中前者是非阻塞的,队列满了就直接返回入队失败;后者是阻塞的,如果队列满了就阻塞入队的线程,直到队列有空闲并插入成功后返回true。这里面会牵涉到内核吗?
    5,反正学完本节发现不知道的更多了,原来自己一直没分清楚过同/异步和是否阻塞。。。疼疼疼

    作者回复: 同步异步可以理解为谁主动,同步就是A问B要东西,总是A主动”伸手“问B要。异步就是A向B注册一个需求,货到了B主动“伸手”把货交给A。
    阻塞队列在阻塞一个线程时,会有系统调用,有系统调用内核就要参与,只是这里的阻塞跟IO的阻塞是两回事。
    其实不要迷茫,理解上面那几张图就行了。😑

    2019-06-12
    6
  • 趙衍
    对于【同步与异步指的是应用程序在与内核通信时,数据从内核空间到应用空间的拷贝内核主动发起还是应用程序触发。】我有一个问题,以同步非阻塞为例,当网卡接收到数据,要将数据送到用户进程时,此时是由用户进程主动向操作系统请求拷贝网卡的数据吗?老师能不能详细介绍一下这个过程?

    作者回复: 是用户进程主动发起read调用,这个是一个系统调用,CPU由用户态切换到内核态,执行内核代码,内核发现这个socket上的数据已经到到了内核空间,将用户线程挂起,然后把数据从内核空间拷贝到用户空间,再唤醒用户线程,read调用返回。

    2019-06-17
    4
  • QQ怪
    老师,信号驱动式 I/O与其他io模型的有啥不一样?

    作者回复: 可以把信号驱动io理解为“半异步”,非阻塞模式是应用不断发起read调用查询数据到了内核没有,而信号驱动把这个过程异步化了,应用发起read调用时注册了一个信号处理函数,其实是个回调函数,数据到了内核后,内核触发这个回调函数,应用在回调函数里再发起一次read调用去读内核的数据。所以是半异步。

    2019-06-13
    4
  • Dovelol
    老师,请教下,”当客户端发起一个http请求时,首先由Acceptor线程run方法中的socket = endpoint.serverSocketAccept();接收连接,然后传递给名称为Poller的线程去侦测I/O事件,Poller线程会一直select,选出内核将数据从网卡拷贝到内核空间的 channel(也就是内核已经准备好数据)然后交给名称为Catalina-exec的线程去处理,这个过程也包括内核将数据从内核空间拷贝到用户空间这么一个过程,所以对于exec线程是阻塞的,此时用户空间(也就是exec线程)就接收到了数据,可以解析然后做业务处理了。
    1.想问下老师我对这个流程的理解对吗,如果不对,哪个地方有问题呢?
    2.老师讲的2个步骤是融合在这里面的吗?
    3.老师说的“当用户线程发起 I/O 操作后,xxx”,这里面应该是哪一步去发起的I/O操作呢?

    作者回复: 1,理解的很准确
    2,对的
    3,Selector发出的select调用就是一个I/O操作。

    2019-06-12
    4
  • zyz
    老师!Tomcat为什么不用Semaphore而是自己实现LimitLatch来限流呢?出于什么考虑?性能?不想强依赖Semaphore?

    作者回复: 可能是Semaphore的实现比较复杂,LimitLatch简单明了够用。

    2019-06-20
    3
  • 🐟🐙🐬🐆🦌🦍🐑�...
    老师 ,今天又看了一遍NioEndpoint 的源码 ,发现PollerEvent 实现了Runnable 接口 ,但是还是直接调用run()方法 没有起线程调用 .在events()函数里 。既然不用线程 还实现runable接口 ,感觉误导读者这是现场运行的
    2019-06-13
    1
    2
  • 疯狂咸鱼
    老师,netty中所说的非阻塞,是服务器端的线程吧?注册到selector中的

    作者回复: 对的

    2019-07-26
    1
  • BingoJ
    老师,那我们常说Java中自身的NIO到底是同步非阻塞,还是IO多路复用呢?

    作者回复: NIO API可以不用Selector,就是同步非阻塞。使用了Selector就是IO多路复用

    2019-06-21
    1
    1
  • 代码搬运工
    ServerSocketChannel 被设置成阻塞模式,也就是说它是以阻塞的方式接收连接的。
    老师:为什么是阻塞的方式?阻塞和非阻塞的区别是什么?这个和多个acceptor有关系吗?阻塞是为了避免共同消费同一个channel吗?

    作者回复: 阻塞方法实现简单一点,跟多个Acceptor没有关系。

    2019-06-19
    2
    1
  • Geek_ebda96
    老师,你好,请问几个问题
    1.limitltach是用来限制应用接收连接的数量,acceptor用来限制系统层面的连接数量,这个限制的先后顺序有关系吗?
    2.sreverscoketchannel设置为阻塞模式是指同时只能处理一个客户端连接请求。即同时只能有一个acceptor调用axcept()方法。把客户端的scoketchannel放入poller的队列里面吗?
    3.内核空间的接收连接是不是对每个连接都产生一个channel,这个channel就是acceptor里接收方法得到的scoketchanmel,后面的poller在用selector的select方法监听内核是否准备就绪才知道监听内核哪个通道?

    作者回复: 1. 首先是LimitLatch限制,应用层处理不过来了,连接才会堆积在操作系统的queue,而queue的大小由acceptCount控制
    2,可以同时有过个Acceptor调用accept方法,accept是线程安全的
    3,对的

    2019-06-18
    1
  • 西兹兹
    请问poller线程数是sever xml那个参数配置? 暂时想到的是acceptorThreadCount对应acceptor线程数和acceptCount是待连接队列,那poller线程数对应哪个参数?

    作者回复: pollerThreadCount

    2019-06-15
    1
  • WL
    有一个问题请教一下老师: NioEndpoint中的events和eventCache之间是啥关系,在events()方法中时把events中的元素放到eventCache中,而在add(final NioChannel socket, final int interestOps)方法中有吧events中的元素取出来放到eventCache中,我看了半天也不太理解这样实现的目的是啥,请老师指点一下。

    作者回复: eventCache是对象池

    2019-06-13
    1
    1
  • 802.11
    老师真的是传道授业解惑者
    2019-06-13
    1
  • QQ怪
    老师我读完这篇文章我瞬间有点顿悟,真的很棒!!!加油!支持老师!
    2019-06-12
    1
  • 代码搬运工
    同时有多个 Poller 线程在运行,每个 Poller 线程都有自己的 Queue。每个 Poller 线程可能同时被多个 Acceptor 线程调用来注册 PollerEvent。
    老师:按照这个意思,有可能一个channel被多个seletor监听,这样的话,重复监听的channel,造成资源浪费。

    作者回复: Acceptor接收到一个新的连接(channel)只注册到一个poller上,只是下一个新连接会注册到另一个poller上

    2019-06-12
    1
  • Yes
    老师我有个问题,Acceptor共享一个ServerSocketChannel,虽然说ServerSocketChannel.accept()是线程安全,但是因为上锁同一时刻只有一个线程能访问accept,并且accept又是阻塞的,那多个Acceptor不是徒增上下文切换么,并不能增加accept速度,难道仅仅是因为Acceptor的run中有其他逻辑需要额外的耗时才需要多个Acceptor么?
    2019-12-03
收起评论
83
返回
顶部