Tomcat连接数和线程池详解:从连接器(Connector)开始
在使用tomcat时,经常会遇到连接数、线程数等配置问题。要真正理解这些概念,首先必须了解Tomcat Connector的连接。
在上一篇文章中我详细写了Tomcat配置文件服务器。 Servlet容器)来处理请求并将生成的Request和Response对象传递给Engine。 Engine处理完请求后,还会通过Connector将响应发送回客户端。
可以说Servlet容器需要Connector来调度和控制处理请求。连接器是 Tomcat 处理请求的支柱。因此,Connector的配置和使用对Tomcat的性能影响很大。本文从Connector开始,讨论Connector相关的一些重要问题,包括NIO/BIO模式、线程池、连接数等。
根据协议不同,Connector可以分为HTTP Connector、AJP Connector等。本文仅讨论 HTTP 连接器。
1。 Nio,传记,APR
1。连接器的协议
连接器在处理 HTTP 请求时使用不同的协议。不同的Tomcat版本支持不同的协议。最典型的协议是BIO、NIO和APR(Tomcat 7支持这3种协议。Tomcat 8增加了对NIO2的支持,而Tomcat 8.5和Tomcat 9.0则取消了对NIO2的支持。BIO支持)。
BIO 阻塞 IO,顾名思义,它阻塞 IO; NIO是non-blocking IO,即非阻塞IO。 APR是Apache Portable Runtime,一个可移植的Apache运行时库。通过使用本地库可以实现高可扩展性和高性能。 apr是在Tomcat上运行高并发应用的首选模式,但需要安装apr、apr-utils和tomcat。 - 本机和其他包。
2。如何指定协议
Connector 使用哪种协议?您可以通过 元素中的协议属性来指定它,也可以使用默认值。
指定的协议值及其关联的协议如下:
- HTTP/1.1:默认值,使用的协议与Tomcat版本相关
- org.apache.coyote.http11.Http11Protocol:BIO
- org.apache.coyote.http11.Http11NioProtocol: NIO
- org.apache.coyote.http11.Http11Nio2Protocol: NIO2
- org.apache.coyote.http11.Http11AprProtocol: APR
如果未指定协议,则使用默认值HTTP/1.1,其含义如下:在Tomcat7中自动选择使用BIO或APR(如果找到APR所需的本地库,则使用APR,否则使用BIO);在Tomcat8中自动选择使用NIO或APR(如果你找到APR需要的本地库)。对于本机库,请使用 APR,否则使用 NIO)。
3。 BIO/NIO有什么区别
无论是BIO还是NIO,处理Connector请求的大致流程都是一样的:
接收连接放入accept队列中(当客户端向服务器发送请求时,如果客户端与操作系统完成三次握手并建立连接,操作系统将连接放入accept队列中);获取连接中请求的数据并生成请求;调用servlet容器处理请求;并返回答案。为了方便下面的解释,首先明确一下连接和请求的关系:连接是在TCP层面(传输层),对应于socket;请求是在HTTP级别(应用层),必须依赖TCP的连接实现;在一个 TCP 连接中可以发送多个 HTTP 请求。
在BIO实现的Connector中,处理请求的主要实体是JIoEndpoint对象。 JIoEndpoint维护了Acceptor和Worker:Acceptor接收socket,然后从Worker线程池中找到一个空闲线程来处理该socket。如果工作线程池中没有空闲线程,Acceptor就会阻塞。其中,Worker是Tomcat自带的线程池。如果通过配置其他线程池,原理与Worker类似。
在NIO实现的Connector中,处理请求的主要实体是NIoEndpoint对象。除了Acceptor和Worker之外,NioEndpoint还使用Poller。处理流程如下图所示(图片来源:http://gearever.iteye.com/blog/1844203)。
![]()
Acceptor收到socket后,并不直接使用Worker中的线程来处理请求,而是先将请求发送给Poller,而Poller是实现NIO的关键。 Acceptor 通过队列向 Poller 发送请求,遵循典型的生产者-消费者模型。在Poller中维护了一个Selector对象;当 Poller 将套接字出列时,它会将其注册到 Selector 中;然后遍历Selector找到可读的socket,并使用Worker中的线程处理相应的请求。与BIO类似,Worker也可以替换为自定义的线程池。
从上面的过程可以看出,NioEndpoint处理请求的过程中,无论acceptor接收到socket还是线程处理请求,仍然采用阻塞的方式;但在“读取套接字并将其传递给 Worker 中的线程”中。这个过程中使用了非阻塞的NIO实现。这是NIO模式和BIO模式的主要区别(其他区别对性能影响较小,暂时忽略)。这种差异在并发量较高的情况下可以显着提高 Tomcat 的效率:
目前,大多数 HTTP 请求都使用长连接(HTTP/1.1 默认 keep-alive 为 true),因此长连接意味着,在当前请求之后, TCP套接字结束时,如果没有新的请求到达,套接字不会立即释放,而是在超时后释放。使用BIO会阻塞“读取socket并将其传递给Worker中的线程”的过程,这意味着当socket等待下一个请求或等待释放时,该socket处理的工作线程将始终处于繁忙状态。无法释放;因此,Tomcat可以同时处理的套接字数量不能超过最大线程数,性能受到严重限制。使用NIO,“读取套接字并将其传递给Worker中的线程”的过程是非阻塞的。当socket等待下一个请求或者等待释放时,不会占用工作线程,大大增加了Tomcat可以同时处理的socket数量。由于最大线程数,并发性能显着提升。
两三个参数:acceptCount、maxConnections、maxThreads
我们看一下Tomcat处理请求的过程:在accept队列中接收连接(当客户端向服务器发送请求时,当客户端完成三路与操作系统的过程握手一旦建立连接,操作系统就会将该连接放入接受队列中);获取连接中请求的数据并生成请求;调用servlet容器处理请求;并返回答案。
相应的,Connector中各个参数的作用如下:
1. AcceptCount
接受队列的长度;当接受队列中的连接数达到acceptCount时,队列已满,并且接受传入的请求。拒绝。默认值为 100。
2。 maxConnections
Tomcat 任何时候接收和处理的最大连接数。当Tomcat接收到的连接数达到maxConnections时,Acceptor线程将不会读取accept队列中的连接;此时accept队列中的线程会被阻塞,直到Tomcat接收到的连接数小于maxConnections。如果设置为-1,则连接数不受限制。
默认值与连接器使用的协议有关:NIO默认值为10000,APR/native默认值为8192,BIO默认值为maxThreads(如果配置了Executor,则默认值为maxThreads)值是 Executor 的 maxThreads)。
在Windows上,APR/native的maxConnections值会自动调整为低于设定值的最大1024的整数倍;如果设置为 2000,则最大值实际上是 1024。
3。 maxThreads
请求处理线程的最大数量。默认值为 200(Tomcat7 和 8)。如果 Connector 绑定到 Executor,则该值将被忽略,因为 Connector 将使用绑定的 Executor 而不是内置线程池来运行任务。
maxThreads 指定最大线程数,而不是实际活动的 CPU 数;事实上,maxThreads的大小远大于CPU核心的数量。这是因为处理请求的线程真正执行计算的时间很少,大部分时间都可以被阻塞,比如等待数据库返回数据、等待硬盘读写数据等。因此,在任何给定时间,只有少数线程实际使用物理 CPU,而大多数线程都在等待;因此,线程数远大于物理核心数是合理的。
换句话说,Tomcat 可以通过使用远大于 CPU 核心数的线程数来保持 CPU 繁忙,并显着提高 CPU 利用率。
4。参数设置
(1) maxThreads 的设置与应用程序的特性以及服务器上的CPU 核心数有关。从前面的介绍我们可以知道maxThreads的数量应该远大于CPU核的数量; CPU核心数越多,maxThreads应该越大;应用程序中CPU 越不密集(IO 越密集),maxThreads 需要越大,这样CPU 才能得到充分利用。 maxThreads的值越大越好。如果maxThreads太大,CPU会花费大量时间切换线程,整体效率会下降。
(2) maxConnections的设置与Tomcat的运行模式有关。如果tomcat使用BIO,maxConnections的值必须和maxThreads一致;如果tomcat使用NIO,maxConnections的值与Tomcat的默认值类似,应该比maxThreads大得多。
(3) 从前面的介绍我们可以知道,tomcat 可以同时处理的连接数是 maxConnections ,而服务器端可以同时接收的连接数是 maxConnections+acceptCount 。 AcceptCount 设置与应用程序在连接过高时如何响应有关。如果设置太大,后续传入请求的等待时间会很长;如果设置太小,后续传入的请求将立即拒绝连接。
3。 ThreadpoolExecutor
Executor元素代表Tomcat中的线程池,可以被其他组件共享;要使用此线程池,组件必须通过 executor 属性指定线程池。
Executor 是 Service 元素的嵌入元素。一般是Connector组件使用线程池;为了使Connector能够使用线程池,Executor元素必须放置在Connector之前。 Executor 和 Connector 配置示例如下:
Executor的主要特点是:
- name:线程池的标签
- maxThreads:线程池中最大活动线程数,默认值为 200(Tomcat7 和 8)
- minSpareThreads:线程池中维护的最小线程数,最小值为 25
- maxIdleTime:线程空闲时的最大空闲时间。超过这个值就关闭线程(除非线程数小于minSpareThreads),单位是ms,默认值是60000(1分钟)
- daemon:是否是后台线程,默认值是true
- threadPriority:线程优先级,默认值为 5
- namePrefix:线程名称的前缀。线程池中的线程名称为:namePrefix + 线程号
IV。查看当前状态
上面介绍了Tomcat连接数和线程数的概念以及如何设置。下面介绍如何查看服务器。中的连接数和线程数。
检查服务器的状态大致可以分为两种方式:(1)使用现成的工具,(2)直接使用Linux命令查看。
现成的工具,比如JDK自带的jconsole工具,可以方便的查看线程信息(另外还可以查看CPU、内存、类、JVM基本信息等)、Tomcat自带的下图是jconsole上查看线程信息的界面:
![]()
下面讲一下如何通过Linux命令行查看服务器上的连接数和线程数。?可以看到有一个连接处于监听状态,监听请求;此外,还有4个现有连接(ESTABLISHED)和2个等待关闭的连接(CLOSE_WAIT)。 ?信息; 27989是线程ID,Java是指执行的Java命令。这是因为当一个tomcat启动时,这个进程内部的所有工作都完成了,包括主线程、垃圾收集线程、Acceptor线程、请求处理线程等。
通过下面的命令可以看到进程中有多少个线程;其中,nlwp表示轻量级进程数。
ps –o nlwp 27989
![]()
可以看到进程中有73个线程;但73并不排除处于空闲状态的线程。要获取实际活动的线程数,可以使用以下语句:
ps -eLo pid ,stat | 27989 | grep 27989 grep 运行 | wc -l
其中 ps -eLo pid ,stat 可以查出所有线程并打印进程号和线程当前状态;两个grep命令分别过滤进程号和线程状态;厕所数数。其中 ps -eLo pid ,stat | 的输出结果grep 27989 如下:
![]()
图中仅部分结果截图; Sl表示大多数线程处于空闲状态。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网