眼见为实:被误导的Tomcat工作原理系列之poller线程是做socket读写的线程吗?

简介: 眼见为实:用页面可视化的方式带你看到Tomcat工作原理,它里面有哪些线程,在每次请求过来的时候做了什么工作?纠正部分网友对于Tomcat工作原理理解的误区,明确Tomcat里到底是哪个线程在做socket读写?

Tomcat的主要工作是:监听用户通过浏览器发送的网络请求,然后把请求连接上你的应用程序,做信息交换。在这个过程中,Tomcat里有acceptor、poller、 exec等等这些线程在做这个工作。
不过网上很多朋友都认为poller是Tomcat里做socket请求数据读写的线程,但是事实真的是这样吗?
image.png
image.png
image.png

以往,Tomcat的工作就像一个黑盒子一样被封装好在底层架构里,我们看不到。本次我们借助了kindling摄像头工具做了个实验,让大家看到每个请求过来之后,所有工作线程的执行实况,以此来确认poller是不是做socket的读写的?
首先,我们先简单回顾一下Tomcat的工作流程:它有两大核心组件,connector和container,
其中container装着你的应用程序代码。打个比方,如果这是剧本杀,用户是玩家,你的程序代码是通关宝藏,那connector就是NPC。
image.png

但是实际上,真正的网络请求,要远比剧本杀复杂的多,所以Tomcat也设计了线程池
来应对大量的并发情况。现在我们再来明确文章一开始提出的疑问:poller线程到底是不是Tomcat里执行socket的读写的线程?下图是我用kindling的摄像头工具捕捉记录了一次请求的执行情况。

image.png
image.png

大家可以先快速通过上面两张图,稍微理解一下这个工具怎么用。我交代一下设置的实验场景:一共做了5个并发请求,每次相隔100ms发出,请求的实现代码是sleep(1000)ms,配置的Tomcat最大的线程数是3。所以这也是一个资源饥饿的场景:并发数有5个,可是线程只有3个,我们一会也可以看看Tomcat是怎么应对的。我这里用工具捕捉的是第一个请求的执行记录。
image.png
image.png

我们可以从上面两张图看到,本次请求,最开始是由acceptor先做cpu-on,建立socket连接,然后把连接事件交给poller管理,大家注意看,poller执行的时间戳是.319,而acceptor是318,所以poller 一定实在acceptor之后才开始执行的。
之后,请求事件被交由Tomcat的线程池分配线程exec-1来执行,也就是图中黑线那段。futex代表该线程夯住,或者说在等待,的确,因为我的实现代码写的就是“sleep(1000)ms”。
image.png

如上图所示,我们可以看到,请求流的读写是由执行线程exec-1来做的,netRead即网路流的读写。同理,请求报文的会写也是由exec-1来做的。如下图所示。(这里有两个netwrite是因为报文可能太大,写了两次,第三个netread是exec-1执行下一个请求的事件,因为时间相差太小,几乎重叠了)
image.png

至此,我们可以明确开始提出的疑问了:Tomcat里对于请求流的读写不是由poller线程,而是由exec执行线程来做的。
我们再继续看,5个并发请求,Tomcat只有3个线程,它会怎么应对?
image.png

如上图所示,等了大概100ms左右,我的第2个请求进来了,Tomcat线程池分配给了exec-3来执行,同理,第三个请求分配给了exec-2来执行。
不过第4、5个请求情况就不一样了。如下图所示,第4、5个请求从客户端发出请求,acceptor建立好连接,poller做好请求事件管理之后,等待了一段时间,它们才被exec线程执行。因为此时Tomcat没有多余的线程了,它们需要等待有exec线程空下来,才能被执行。
image.png

这里反映一种什么现象?对于客户端来说,我前3个请求都很正常,可是第4、5个请求是有一点慢的,它在等待了一段时间之后才开始执行。不过对于服务端来说,它的响应时间没有任何问题,因为服务端的响应时间,不是从建立连接开始,而是从exec线程真正处理这个请求开始计算的。
这也是我们生产环境上常见但是由十分难以排查的资源饥饿的问题,因为从表面上你很难发现症结。但是如果你懂一些Tomcat的原理,再加上kindling摄像头工具的辅助,定位起来是不是就容易很多了?

image.png
image.png
image.png

本次分享暂时到这里了,希望能给大家带来一些收获,也许有些同学也有了更多疑问需要被解答,那就请继续关注我们,或者扫描二维码联系我们,谢谢大家~

目录
相关文章
|
3月前
|
安全 Java 数据库
一天十道Java面试题----第四天(线程池复用的原理------>spring事务的实现方式原理以及隔离级别)
这篇文章是关于Java面试题的笔记,涵盖了线程池复用原理、Spring框架基础、AOP和IOC概念、Bean生命周期和作用域、单例Bean的线程安全性、Spring中使用的设计模式、以及Spring事务的实现方式和隔离级别等知识点。
|
3月前
|
编解码 网络协议 API
Netty运行原理问题之Netty的主次Reactor多线程模型工作的问题如何解决
Netty运行原理问题之Netty的主次Reactor多线程模型工作的问题如何解决
|
2月前
|
存储 缓存 Java
什么是线程池?从底层源码入手,深度解析线程池的工作原理
本文从底层源码入手,深度解析ThreadPoolExecutor底层源码,包括其核心字段、内部类和重要方法,另外对Executors工具类下的四种自带线程池源码进行解释。 阅读本文后,可以对线程池的工作原理、七大参数、生命周期、拒绝策略等内容拥有更深入的认识。
143 29
什么是线程池?从底层源码入手,深度解析线程池的工作原理
|
1月前
|
前端开发 Java 应用服务中间件
21张图解析Tomcat运行原理与架构全貌
【10月更文挑战第2天】本文通过21张图详细解析了Tomcat的运行原理与架构。Tomcat作为Java Web开发中最流行的Web服务器之一,其架构设计精妙。文章首先介绍了Tomcat的基本组件:Connector(连接器)负责网络通信,Container(容器)处理业务逻辑。连接器内部包括EndPoint、Processor和Adapter等组件,分别处理通信、协议解析和请求封装。容器采用多级结构(Engine、Host、Context、Wrapper),并通过Mapper组件进行请求路由。文章还探讨了Tomcat的生命周期管理、启动与停止机制,并通过源码分析展示了请求处理流程。
|
1月前
|
负载均衡 应用服务中间件 Apache
Tomcat负载均衡原理详解及配置Apache2.2.22+Tomcat7
Tomcat负载均衡原理详解及配置Apache2.2.22+Tomcat7
42 3
|
1月前
|
Java 编译器 程序员
【多线程】synchronized原理
【多线程】synchronized原理
59 0
|
1月前
|
Java API
【多线程】乐观/悲观锁、重量级/轻量级锁、挂起等待/自旋锁、公平/非公锁、可重入/不可重入锁、读写锁
【多线程】乐观/悲观锁、重量级/轻量级锁、挂起等待/自旋锁、公平/非公锁、可重入/不可重入锁、读写锁
34 0
|
1月前
|
Java 应用服务中间件 API
nginx线程池原理
nginx线程池原理
37 0
|
2月前
|
存储 缓存 Java
JAVA并发编程系列(11)线程池底层原理架构剖析
本文详细解析了Java线程池的核心参数及其意义,包括核心线程数量(corePoolSize)、最大线程数量(maximumPoolSize)、线程空闲时间(keepAliveTime)、任务存储队列(workQueue)、线程工厂(threadFactory)及拒绝策略(handler)。此外,还介绍了四种常见的线程池:可缓存线程池(newCachedThreadPool)、定时调度线程池(newScheduledThreadPool)、单线程池(newSingleThreadExecutor)及固定长度线程池(newFixedThreadPool)。
|
3月前
|
存储 NoSQL Java
线程池的原理与C语言实现
【8月更文挑战第22天】线程池是一种多线程处理框架,通过复用预创建的线程来高效地处理大量短暂或临时任务,提升程序性能。它主要包括三部分:线程管理器、工作队列和线程。线程管理器负责创建与管理线程;工作队列存储待处理任务;线程则执行任务。当提交新任务时,线程管理器将其加入队列,并由空闲线程处理。使用线程池能减少线程创建与销毁的开销,提高响应速度,并能有效控制并发线程数量,避免资源竞争。这里还提供了一个简单的 C 语言实现示例。

相关实验场景

更多
下一篇
无影云桌面