Code前端首页关于Code前端联系我们

Tomcat连接数和线程池详解:从连接器(Connector)开始

terry 2年前 (2023-09-28) 阅读数 74 #未命名

在使用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)。

Tomcat 连接数与线程池详解:从连接器(Connector)说起

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上查看线程信息的界面:

Tomcat 连接数与线程池详解:从连接器(Connector)说起

下面讲一下如何通过Linux命令行查看服务器上的连接数和线程数。?可以看到有一个连接处于监听状态,监听请求;此外,还有4个现有连接(ESTABLISHED)和2个等待关闭的连接(CLOSE_WAIT)。 ?信息; 27989是线程ID,Java是指执行的Java命令。这是因为当一个tomcat启动时,这个进程内部的所有工作都完成了,包括主线程、垃圾收集线程、Acceptor线程、请求处理线程等。

通过下面的命令可以看到进程中有多少个线程;其中,nlwp表示轻量级进程数。

ps –o nlwp 27989

Tomcat 连接数与线程池详解:从连接器(Connector)说起

可以看到进程中有73个线程;但73并不排除处于空闲状态的线程。要获取实际活动的线程数,可以使用以下语句:

ps -eLo pid ,stat | 27989 | grep 27989 grep 运行 | wc -l

其中 ps -eLo pid ,stat 可以查出所有线程并打印进程号和线程当前状态;两个grep命令分别过滤进程号和线程状态;厕所数数。其中 ps -eLo pid ,stat | 的输出结果grep 27989 如下:

Tomcat 连接数与线程池详解:从连接器(Connector)说起

图中仅部分结果截图; Sl表示大多数线程处于空闲状态。

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

热门