Tomcat 处理请求的过程


请求在 tomcat 服务器的处理过程(BIO 模式)

客户端与服务端完成三次握手建立了连接,连接信息会存放在 ServerSocket 连接请求的队列中(队列长度为 acceptCount)

如果提交到线程池的任务数没有超过 maxConnections,那么就 ServerSocket.accept() 返回 socket 对象,封装为任务提交到线程池;

如果提交的任务数超过了 maxConnections,则阻塞

任务提交到线程池后,分三种情况:

线程数 <= minSpareThreads:不管有没有空闲线程,都新建线程来处理任务
minSpareThreads < 线程数 < maxThreads:新任务会优先使用空闲线程,如果没有空闲线程就新建线程
线程数 == maxThreads:新任务就会在 Connector 创建的 ServerSocket 队列中堆积起来,直到到达最大的配置值(acceptCount 属性值)
若队列已满,任何再来的请求将会收到 connection refused 错误,直到有可用的资源来处理它们

当任务被处理完后,则销毁任务以及任务中的 socket 对象,该连接被释放

Connector 的 protocol(协议)
Connector 在处理 HTTP 请求时,会使用不同的 protocol。不同的 Tomcat 版本支持的 protocol 不同,其中最典型的 protocol 包括BIO、NIO 和 APR(Tomcat7 中支持这 3 种,Tomcat8 增加了对 NIO2 的支持,而到了 Tomcat8.5 和 Tomcat9.0,则去掉了对 BIO 的支持)。

BIO(Blocking IO):阻塞的 IO

NIO(Non-blocking IO):非阻塞的 IO

APR(Apache Portable Runtime):是 Apache 可移植运行库,利用本地库可以实现高可扩展性、高性能;

Apr 是在 Tomcat 上运行高并发应用的首选模式,但需要安装 apr、apr-utils、tomcat-native 等包。


BIO
在 BIO 实现的 Connector 中,处理请求的主要实体是 JIoEndpoint 对象。

JIoEndpoint 维护了 Acceptor 和 Worker:

Acceptor 接收 socket,然后从 Worker 线程池中找出空闲的线程处理 socket,如果 worker 线程池没有空闲线程,则 Acceptor 将阻塞。
Worker 是 Tomcat 自带的线程池,如果通过配置了其他线程池,原理与 Worker 类似。

NIO
在 NIO 实现的 Connector 中,处理请求的主要实体是 NIoEndpoint 对象。

NIoEndpoint 中除了包含 Acceptor 和 Worker 外,还使用了 Poller,处理流程如下图所示:

Acceptor 接收 socket 后,不是直接使用 Worker 中的线程处理请求,而是先将请求发送给了 Poller,而 Poller 是实现 NIO 的关键。Acceptor 向 Poller 发送请求通过队列实现,使用了典型的生产者-消费者模式。
在 Poller 中,维护了一个 Selector 对象;当 Poller 从队列中取出 socket 后,注册到该 Selector 中;然后通过遍历 Selector,找出其中可读的 socket,并使用 Worker 中的线程处理相应请求。
与 BIO 类似,Worker 也可以被自定义的线程池代替。
在 NIoEndpoint 处理请求的过程中,无论是 Acceptor 接收 socket,还是线程处理请求,使用的仍然是阻塞方式;但在 ”读取socket并交给Worker中的线程” 的这个过程中,使用非阻塞的 NIO 实现,这是 NIO 模式与 BIO 模式的最主要区别(其他区别对性能影响较小,暂时略去不提)。而这个区别,在并发量较大的情形下可以带来 Tomcat 效率的显著提升。


影响并发的 tomcat 参数
maxConnections :Tomcat 在任意时刻接收和处理的最大连接数(可以提交给线程池的最大任务数)

当 Tomcat 接收的连接数达到 maxConnections 时,Acceptor 线程不会读取 accept 队列中的连接(socket);这时 accept 队列中的线程会一直阻塞着,直到 Tomcat 接收的连接数小于 maxConnections。

如果设置为 -1,则连接数不受限制。

默认值与连接器使用的协议有关:

NIO 的默认值是 10000

APR/native 的默认值是 8192

在windows下,APR/native 的 maxConnections 值会自动调整为设置值以下最大的 1024 的整数倍

如设置为 2000,则最大值实际是 1024

BIO 的默认值为 maxThreads(如果配置了 Executor,则默认值是 Executor 的 maxThreads)

acceptCount :允许的最大并发连接数(瞬时连接、瞬时并发数),为 ServerSocket 连接请求的队列长度,默认值为 100

请求连接在任务队列中时,客户端显示为浏览器显示“转圈”

当 accept 队列中连接的个数达到 acceptCount 时,队列满,进来的请求一律被拒绝。

实际场景中,常见的表象是 nginx 响应 502,Tomcat 中没有任何 access 日志,此时应该调大该值。

minProcessors:服务器启动时,线程池创建的最少线程数

maxProcessors(maxThreads ):线程池最大连接线程数。默认值为 200

线程数小于此数时,每次来任务若有空闲线程,使用空闲线程处理,如果没有空闲线程则新建线程处理

如果该 Connector 绑定了 Executor,这个值会被忽略,因为该 Connector 将使用绑定的 Executor,而不是内置的线程池来执行任务。

注:

maxThreads 规定的是最大的线程数目,并不是实际 running 的 CPU 数量;

实际上,maxThreads 的大小比 CPU 核心数量要大得多。

因为处理请求的线程真正用于计算的时间可能很少,大多数时间可能在阻塞,如等待数据库返回数据、等待硬盘读写数据等。

因此,在某一时刻,只有少数的线程真正的在使用物理 CPU,大多数线程都在等待;

故线程数远大于物理核心数才是合理的。

换句话说,Tomcat 通过使用比 CPU 核心数量多得多的线程数,可以使 CPU 忙碌起来,大大提高 CPU 的利用率

minSpareThreads :线程池最小空闲线程数(多余的空闲线程都将杀死)。默认值为 25

线程数小于此数时,每次来任务都新建线程处理

maxSpareThreads :线程池最大空闲线程数

一旦创建的线程超过这个值,Tomcat 就会关闭不再需要的 socket 线程

maxIdLeTime:一个线程空闲多久算是一个空闲线程,单位:毫秒

connectionTimeout :网络连接超时。单位:毫秒。默认值为 60000ms(60秒)

设置为 0 表示永不超时,但这样设置有隐患的。通常设置为 30000 毫秒或使用默认值

disableUploadTimeout :禁用上传超时,主要用于大数据上传时,允许 Servlet 容器正在执行使用一个较长的连接超时值,以使 Servlet 有较长的时间来完成它的执行,默认值为 false

enableLookups :是否反查域名,取值为:true 或 false

若为 true,则可以通过调用 request.getRemoteHost() 进行 DNS 查询来得到远程客户端的实际主机名

若为 false,则不进行DNS查询,而是返回其 ip 地址

为了提高处理能力,应设置为 false
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值