tomcat

参考:Tomcat的3个参数acceptCount、maxConnections、maxThreads

Tomcat 的核心组件

Tomcat 由 2 大核心组件组成:Connector、Container

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

补充说明:

maxThreads 和 acceptCount 属性对 tomcat 能同时处理的请求数和请求响应时间有直接的影响。 无论 acceptCount 值为多少,maxThreads 直接决定了实际可同时处理的请求数。 而不管 maxThreads 如何,acceptCount 则决定了有多少请求可等待处理。 然而,不管是可立即处理请求还是需要放入等待区,都需要 tomcat 先接受该请求(即接受客户端的连接请求,建立socketchannel),那么 tomcat 同时可建立的连接数(maxConnections 属性值)也会影响可同时处理的请求数。

如何设置 acceptCount 、maxConnections、maxThreads 的值:

CPU 越不密集(或 IO 越密集),maxThreads 应该越大 maxConnections 的设置与 Tomcat 的运行模式有关 如果 tomcat 使用的是 BIO,那么 maxConnections 的值应该与 maxThreads 一致(默认值为 maxThreads) 如果 tomcat使用的是 NIO,maxConnections 值应该远大于 maxThreads(默认值为 10000) Tomcat 能够接收的连接数 = maxThreads + acceptCount acceptCount 的设置,与应用在连接过高情况下希望做出什么反应有关系

如果设置过大,后面进入的请求等待时间会很长如果设置过小,后面进入的请求立马返回 connection refused

在线用户数、连接数、瞬时并发数、线程数的区别

在线用户数 = 连接数 + 静态用户数(已登录,但连接已断开,只是在浏览静态数据)(有session对象,没有socket对象)连接数 = 已接受连接数 + 瞬时并发数(acceptCount:在连接队列里等待的socket对象数)已接受连接数 = 线程数(RUNNABLE状态)(正在处理)+ 任务队列中的任务数(已接受,待处理)

影响并发的其他限制因素

Tomcat 的运行模式

BIO(阻塞式的 Socket 通信)模式 Tomcat8 以下版本,默认的 HTTP 实现是采用 BIO 模式,每个请求都需要创建一个线程处理 这种模式下的并发量受到线程数的限制,不大适合高并发,但技术成熟。 每个进程中的线程数受制于操作系统的内核参数设置:

Windows 主机每个进程中的线程数不允许超过 2000Linux 主机每个进程中的线程数不允许超过 1000 NIO模式(非阻塞式的 Socket 通信) Tomcat8 以上版本,默认使用的就是 NIO 模式,在性能上高于阻塞式的,每个请求也不需要创建一个线程进行处理,并发能力比前者高。 APR 模式(全称 Apache Portable Runtime) 是 Tomcat 生产环境运行的首选方式。但必须要安装 APR 和 Native,直接启动就支持 APR。 APR 是从操作系统级别解决异步 IO 问题。APR 的本质就是使用 JNI 技术调用操作系统底层的 IO 接口,所以需要提前安装所需要的依赖。 如果操作系统未安装 APR 或者 APR 路径未指到 Tomcat 默认可识别的路径,则 APR 模式无法启动,自动切换启动 NIO 模式。 注:APR 模式可以提升 Tomcat 对静态文件的处理性能,当然也可以采用动静分离。 JVM 调优(tomcat 可以使用的内存) Tomcat 是运行在 JVM 上的,所以对 JVM 的调优也是非常有必要的 在 Java 中每开启一个线程需要耗用 1MB 的 JVM 内存空间用于作为线程栈之用 tomcat 默认可以使用的内存为128MB,在并发量较大的应用项目中,这点内存是不够的,需要修改 JVM 参数调优

Unix下,在文件{tomcat_home}/bin/catalina.sh的前面,增加如下设置: JAVA_OPTS=‘-Xms【初始化内存大小】 -Xmx【可以使用的最大内存】’ 需要把这个两个参数值调大。例如:JAVA_OPTS=‘-Xms256m -Xmx512m’ 表示初始化内存为 256MB,可以使用的最大内存为 512MB 一台主机允许的连接数、线程数、内存大小、硬件性能和 CPU 数量,都会限制实际并发数 并发能力还与应用的逻辑密切相关,如果逻辑很复杂需要大量的计算,那并发能力势必会下降。 如果每个请求都含有很多的数据库操作(或其他中间件的连接),那么对于数据库的性能要求也是非常高的。 对于单台数据库服务器来说,允许客户端的连接数量是有限制的(数据库读写的并发能力) 建议当某个应用拥有 250 个以上并发的时候,应考虑应用服务器的集群

拓展

tomcat 服务器 server.xml 文件

description="User database that can be updated and saved"

factory="org.apache.catalina.users.MemoryUserDatabaseFactory"

pathname="conf/tomcat-users.xml" />

minSpareThreads="25" maxSpareThreads="75"

connectionTimeout="20000" disableUploadTimeout="true"

enableLookups="false" redirectPort="8443" />

xmlValidation="false" xmlNamespaceAware="false">

注:

tomcat 启动后会默认占用 8080,8009和 8005 三个端口

8080 端口负责建立 HTTP 连接。在通过浏览器访问 Tomcat服务器的 Web 应用时,使用的就是这个连接器8009 端口负责和其他 HTTP服务器建立连接。在把 Tomcat 与其他 HTTP 服务器集成时就需要用到这个连接器8005 端口用来向 Tomcat 发布 shutdown 命令的

对网络端口的理解

实际上,电脑在网卡上的硬件网络端口只有一个。常说的 1-65535 号端口,并不是真的有这么多个硬件端口,硬件端口实际上只有一个,访问所有端口的数据包都发往这个硬件端口。硬件端口接收到数据包之后进行解析,然后通知监听对应端口的socket对象来取数据。

并发量计算

参考: 并发量计算

几个概念:业务并发用户数、最大并发访问数、系统用户数、同时在线用户数

假设一个 OA 系统有 1000 用户,这就是系统用户数最高峰同时有 500 人在线,是“同时在线人数”,也称作“最大业务并发用户数”500 个同时使用系统用户中20%查看系统公告,不构成压力;20%填写表格(只在提交时才会请求,填写对服务器不构成压力);40%在发呆(什么都没做);20%用户不停从一个页面跳转另一个页面(只有这20%对服务器产生了压力)服务器实际压力(能承受的最大并发访问数),既取决于业务并发用户数,还取决于用户的业务场景,**这些可以通过对服务器日志的分析得到,**一般只需要分析典型业务(用户常用,最关注的业务操作)。

估算业务并发用户数的公式(测试人员一般只关心业务并发用户数)

C = nL / T C^ = C + 3 × (C的平方根)

C 是平均的业务并发用户数 n 是 login session 的数量

L 是 login session 的平均长度 T 是指考察的时间段长度

C^ 是指业务并发用户数的峰值

该公式的得出是假设用户的 login session 产生符合泊松分布而估算得到。

假设:OA 系统有1000用户,每天400个用户发访问,每个登录到退出平均时间2小时,在1天时间内用户只在8小时内使用该系统。

C = 400 × 2 / 8 = 100

C^ = 100 + 3 × (100的平方根) = 100 + 3 × 10 = 130

另外,如果知道平均每个用户发出的请求数 u,则系统吞吐量可以估算为 u × C

注意:

精确估算,还要考虑用户业务操作存在一定的时间集中性(比如上班后 1 小时内是 OA 系统高峰期),采用公式计算仍然会存在偏差针对例子 OA 系统可以把 1 小时设定为考察时间的粒度,将一天 8 小时划分为 8 个区间,这样可以解决业务操作存在集中性问题,更趋于精准,偏差更小。

系统吞度量要素

参考:系统吞吐量(TPS)、用户并发量、性能测试概念和公式

系统吞吐量的几个重要参数:QPS(TPS)、并发数、响应时间

QPS(TPS)= 并发数 / 平均响应时间

QPS(TPS):每秒钟request / 事务数并发数: 系统同时处理的request / 事务数响应时间: 一般取平均响应时间 (很多人经常会把并发数和 TPS 理解混淆) 一个系统吞吐量通常由 QPS(TPS)、并发数两个因素决定。 每套系统这两个值都有一个相对极限值,在应用场景访问压力下,只要某一项达到系统最高值,系统的吞吐量就上不去了 如果压力继续增大,系统的吞吐量反而会下降,原因是系统超负荷工作,上下文切换、内存等等其它消耗导致系统性能下降。 系统响应时间,由 CPU 运算、IO、外部系统响应等等组成。

tomcat 高并发配置与优化

参考:tomcat高并发配置与优化

好文阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: