本文皆为tomcat配置文件server.xm中Service模块配置优化
1. HTTP/1.1 协议的协议处理器选择
tomcat启动的时候,可以通过日志看到Connector使用的是哪一种协议处理器
1.1. Http11AprProtocol(推荐)
xml
<Connector protocol="org.apache.coyote.http11.Http11AprProtocol" />
- 基于 Apache Portable Runtime(APR)库实现的协议处理器;
- 使用基于 epoll 或 kqueue 的 I/O 模型,支持非阻塞 I/O 和事件驱动模型,具有很好的性能和可伸缩性;
- APR 是一种跨平台的 C 库,在性能和资源利用率方面比 Java 实现更优秀;
- 适用于高并发、高吞吐量 Web 应用场景,特别是对于低延时、高并发的短连接请求具有更好的性能表现。
虽然apr在性能、资源利用等各方面综合能力都比nio2好,但需要注意的是:如果是docker模式使用tomcat则可直接使用apr模式,无需安装arp库;如果非docker环境,则需先安装APR库,并配置好相关的环境变量和库文件路径,比较麻烦。此外,APR 也有一些局限性,如不支持 Windows 系统、需要手动编译等。
1.2. Http11Nio2Protocol(小服务器适用)
xml
<Connector protocol="org.apache.coyote.http11.Http11Nio2Protocol" />
- 基于 NIO2(Java 7 版本引入)实现的高性能、可伸缩的协议处理器;
- 支持非阻塞 I/O 和事件驱动模型,可以处理大量并发连接;
- 安全性和稳定性较高,因为它是纯 Java 实现,不依赖任何外部库或操作系统;
- 适用于高并发、高吞吐量的 Web 应用场景,特别是对于长连接、异步请求等复杂场景的性能表现更好。
相比之下,Http11Nio2Protocol更加易用和便捷(主要是不需要安装依赖库),但它相对于Http11AprProtocol性能、资源利用等略有劣势。
2.开启线程池
默认tomcat不开启线程池,即n个请求就是n个线程。而线程池是一种用于管理和复用线程的机制,它可以有效地管理线程的创建、执行和销毁,提供了一种线程资源的调度和管理方式。线程池在多线程编程中具有以下几个重要的作用:
- 提高性能和效率:线程池可以避免频繁地创建和销毁线程,从而减少了线程创建和上下文切换的开销,提高了系统的性能和效率。通过重复利用已创建的线程,线程池可以更好地利用可用的系统资源。
- 控制并发线程数量:线程池可以限制同时执行的线程数量,通过设置线程池的大小和可用的线程数,可以控制并发线程的数量,防止系统被过多的线程占用,避免资源耗尽和系统崩溃等问题。
- 提供任务队列和调度机制:线程池通常会包含一个任务队列,用于存储等待执行的任务。当线程池中有空闲线程时,它会从任务队列中获取任务并执行。如果任务队列已满,新提交的任务可能会被暂时存储或根据配置进行拒绝处理。线程池可以根据自定义的调度策略来管理任务的执行顺序和优先级。
- 提供线程管理和监控功能:线程池可以提供对线程的生命周期进行管理,包括线程的创建、销毁和状态管理。通过线程池,可以方便地监控线程的执行情况、统计线程的使用情况和性能指标,便于系统调优和故障排查。
总之,线程池在多线程编程中起着重要的作用,可以提高系统的性能、提供并发控制和任务调度机制,并简化线程管理和监控的工作。合理使用线程池可以避免线程过多造成的资源浪费和性能下降,同时提高代码的可维护性和可扩展性。
1.1配置开启
Service模块开启线程池
xml
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="500" minSpareThreads="30"/>
Service模块链接器指定调用线程池
xml
<Connector executor="tomcatThreadPool"
port="8080" protocol..../>
最终配置如下图
1.2. 参数配置
当connector配置的参数与executor参数重复时,将以connector为主
参数 | 说明 |
---|---|
protocol | 协议处理器,极致性能、资源利用请使用apr |
maxThreads | Tomcat使用线程来处理接收的每个请求,这个值表示Tomcat可创建的最大的线程数,即支持的最大并发连接数,默认值是200,一般建议在 500 ~ 800 |
maxQueueSize | 最大等待队列数,maxThreads满时,请求会进入等待队列 |
acceptCount | 控制连接器可以接收的最大请求数量。当所有线程都处于繁忙状态且请求队列已满时,新的请求将被拒绝或丢弃。这个属性用于限制连接器队列中等待处理的请求数量,避免过多的请求导致服务器资源耗尽,即maxQueueSize满时,会触发此参数,此参数满就会拒绝后续的请求 |
enableLookups | 使用DNS查询,用于控制是否启用 DNS 查找。如果启用了 enableLookups,Tomcat 将会尝试将客户端的 IP 地址解析为主机名,这可能会导致一些性能问题。如果禁用了 enableLookups,则 Tomcat 不会进行 DNS 查找,从而可以避免这些性能问题 |
minSpareThreads | 最小空闲线程数,Tomcat 启动时的初始化的线程数,表示即使没有人使用也开这么多空线程等待,默认值是 10。 |
prestartminSpareThreads | 在 Tomcat 初始化的时候就初始化 minSpareThreads 的参数值,如果不等于 true,minSpareThreads 的值就没啥效果了 |
maxIdleTime | 如果当前线程大于初始化线程,那空闲线程存活的时间,单位毫秒,默认60000=60秒=1分钟 |
maxPostSize | 设置由容器解析的URL参数的最大长度,小于0为禁用这个属性,默认为2097152(2M) 请注意, FailedRequestFilter 过滤器可以用来拒绝达到了极限值的请求 |
maxHttpHeaderSize | http请求头信息的最大程度,超过此长度的部分不予处理。一般8K |
compression | 是否启用GZIP压缩 on为启用(文本数据压缩) off为不启用, force 压缩所有数据 |
compressableMimeType | 压缩的数据类型 |
acceptorThreadCount | protocol为nio或nio2才生效,用于指定接受连接的线程数量,默认1,但是一般设置2 |
disableUploadTimeout | 上传时是否使用超时机制 |
1.2.1. maxThreads、acceptCount、maxQueueSize触发的顺序
在 Tomcat 中,请求的处理顺序如下:
- 当有新的请求到达时,Tomcat 首先检查连接器线程池中是否有可用的空闲线程。
- 如果有可用的空闲线程,Tomcat 会立即将请求分配给其中一个线程进行处理。
- 如果所有的线程都处于繁忙状态,Tomcat 将把请求放入连接器的请求队列中等待处理。
- 当请求进入请求队列时,Tomcat 会根据 maxQueueSize 参数来判断队列是否已满。
- 如果队列未满,请求将被添加到队列的末尾,等待线程池中的线程变为可用时再进行处理。
- 如果队列已满,Tomcat 将根据 acceptCount 参数来决定是否接受该请求。
- 如果请求被接受,Tomcat 将继续等待线程池中的线程变为可用后进行处理。
- 如果请求被拒绝或丢弃,客户端可能会收到连接失败或超时的错误。
因此,maxThreads 控制线程池中线程的数量,maxQueueSize 控制请求队列的容量,而 acceptCount 用于限制连接器可以接受的请求数量。在处理请求时,Tomcat 首先会尽量使用线程池中的空闲线程,然后将请求放入请求队列中,最后根据 acceptCount 来决定是否接受请求。