tomcat和nginx性能分析和调优(一)

509 阅读8分钟

本文已参与「新人创作礼」活动.一起开启掘金创作之路。

tomcat和nginx性能分析和调优

中间件对性能的影响

通常项目部署都会使用tomcat,一个请求过来先经过nginx,然后到网关,网关再到微服务,服务再到服务之间的调用,中间经过了层层链路。本文针对tomcat和nginx进行分析

中间件并发数平均响应90%响应时间最大响应时间错误率(1s内响应)
B2003106677070
A->B20046998110084.5%
网关->A->B2008761513163238.00%
nginx->网关->A->B2009951602168838.00%

通过上面这组数据可以看出每多一个中间件接口响应就慢一些,当中间件越堆越多时,你再怎么取改代码都是没有用的,需要对整个调用链做一个排差,找出最慢的那个服务做优化。

压测工具

jemter介绍

聚合报告参数介绍

  • Label:每个 JMeter 的 element(例如 HTTP Request)都有一个 Name 属性,这里显示的就是 Name 属性的值。
  • #Samples:表示你这次测试中一共发出了多少个请求,如果模拟10个用户,每个用户迭代10次,那么这里显示100。
  • Average:平均响应时间——默认情况下是单个 Request 的平均响应时间,当使用了 Transaction Controller 时,也可以以Transaction 为单位显示平均响应时间。
  • Median:中位数,也就是 50% 用户的响应时间。
  • 90%95%99% Line: 90% 95% 99% 用户的响应时间。
  • Min:最小响应时间。
  • Max:最大响应时间。
  • Error% :本次测试中出现错误的请求的数量/请求的总数。
  • Throughput:吞吐量——默认情况下表示每秒完成的请求数(Request per Second),当使用了 Transaction Controller 时,也可以表示类似 LoadRunner 的 Transaction per Second 数。
  • Received KB/Sec:每秒从服务器端接收到的数据量。

Tomcat

Tomcat主要的配置参数

SpringBoot项目默认配置可以在org.springframework.boot.autoconfigure.web.ServerProperties类中查看

maxConnections 最大连接数

这个参数是指在同一时间,Tomcat 能够接受的最大连接数。对于 Java 的阻塞式 BIO,默认值是 maxthreads 的值;可以通过配置 Executor 执行器来修改这个值。
对于 Java 新的 NIO 模式,maxConnections 默认值是 10000。
对于 windows 上 APR/native IO模式,maxConnections 默认值为 8192,这是出于性能原因,如果配置的值不是 1024 的倍数,maxConnections 的实际值将减少到 1024 的最大倍数。
如果设置为 -1,则禁用 maxconnections 功能,表示不限制tomcat容器的连接数。

简单来说就是 Tomcat 总共允许建立多少连接。

maxThreads 最大线程数

每一次 Http 请求到达 Web 服务,Tomcat 都会创建一个线程来处理该请求,最大线程数决定了 Web 服务同时可以处理多少请求。maxThreads 默认值为 200,建议增加,但是增加线程是有成本的,更多的线程代表会有更多的上下文切换,也意味着 JVM 会分配更多的内存。

acceptCount 排队连接数

当 Tomcat 的最大连接数 maxConnections 被占满之后,后续的请求就会进行排队,排队的最大数量就是 acceptCount,举个例子,当前 maxConnections 为 10,acceptCount 为 5,并且 maxConnections 已经使用了 10,那么后续的请求就会排队,每来一个请求,acceptCount 就会 +1 ,当 acceptCount 增加到 5 ,在后续的请求就会被直接放弃。

connection-timeout 连接建立时间

HTTP 协议运行在 TCP 之上,所以每次请求到来,客户端和服务端会建立一次 TCP 连接,建立连接需要三次握手,所以就需要一定的时间,connection-timeout 限制了连接建立的时间,当建立连接时间超过这个值,连接就会建立失败。默认为 20000ms。

Tomcat 的四种线程模型

IO模型描述
BIO同步阻塞式IO,即Tomcat使用传统的java.io进行操作。该模式下每个请求都会创建一个线程,对性能开销大,不适合高并发场景。优点是稳定,适合连接数目小且固定架构。
NIO同步非阻塞式IO,jdk1.4 之后实现的新IO。该模式基于多路复用选择器监测连接状态在同步通知线程处理,从而达到非阻塞的目的。比传统BIO能更好的支持并发性能。Tomcat 8.0之后默认采用该模式
APR全称是 Apache Portable Runtime/Apache可移植运行库),是Apache HTTP服务器的支持库。可以简单地理解为,Tomcat将以JNI的形式调用Apache HTTP服务器的核心动态链接库来处理文件读取或网络传输操作。使用需要编译安装APR 库
AIO (asynchronous I/O)异步非阻塞式IO,jdk1.7后之支持 。与nio不同在于不需要多路复用选择器,而是请求处理线程执行完程进行回调调知,已继续执行后续操作。Tomcat 8之后支持。

使用指定IO模型

Tomcat容器

配置 server.xml 文件当中的 修改即可。默认配置 8.0 protocol=“HTTP/1.1” 8.0 之前是 BIO 8.0 之后是NIO

BIO

protocol=“org.apache.coyote.http11.Http11Protocol“

NIO

protocol=”org.apache.coyote.http11.Http11NioProtocol“

AIO

protocol=”org.apache.coyote.http11.Http11Nio2Protocol“

APR

protocol=”org.apache.coyote.http11.Http11AprProtocol“

SpringBoot内置容器

SpringBoot内置的Tomcat默认是基于NIO来实现的

import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.Connector;
import org.apache.coyote.ProtocolHandler;
import org.apache.coyote.http11.AbstractHttp11Protocol;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;

/**
 * @author hzt
 * @version 1.0.0
 * @ClassName AppTomcatConnectorCustomizer.java
 * @createTime 2021年07月08日 09:33:00
 */
@Slf4j
@Component
public class AppTomcatConnectorCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
    @Override
    public void customize(ConfigurableServletWebServerFactory factory) {
        ((TomcatServletWebServerFactory) factory).setProtocol("org.apache.coyote.http11.Http11Nio2Protocol");
        ((TomcatServletWebServerFactory) factory).addConnectorCustomizers(new TomcatConnectorCustomizer() {
            @Override
            public void customize(Connector connector) {
                ProtocolHandler protocol = connector.getProtocolHandler();
                log.info("Tomcat({})  -- MaxConnection:{};MaxThreads:{};MinSpareThreads:{}", //
                        protocol.getClass().getName(), //
                        ((AbstractHttp11Protocol<?>) protocol).getMaxConnections(), //
                        ((AbstractHttp11Protocol<?>) protocol).getMaxThreads(), //
                        ((AbstractHttp11Protocol<?>) protocol).getMinSpareThreads());

            }
        });
    }
}

Tomcat connector 并发参数

名称描述
acceptCount等待最大队列
address绑定客户端特定地址,127.0.0.1
bufferSize每个请求的缓冲区大小。bufferSize * maxThreads
compression是否启用文档压缩
compressableMimeTypestext/html,text/xml,text/plain
connectionTimeout客户发起链接 到 服务端接收为止,中间最大的等待时间
connectionUploadTimeoutupload 情况下连接超时时间
disableUploadTimeouttrue 则使用connectionTimeout
enableLookups禁用DNS查询 true
keepAliveTimeout当长链接闲置 指定时间主动关闭 链接 ,前提是客户端请求头 带上这个 head"connection" " keep-alive"
maxKeepAliveRequests最大的 长连接数
maxHttpHeaderSize
maxSpareThreadsBIO 模式下 最多线闲置线程数
maxThreads(执行线程)最大执行线程数
minSpareThreads(初始线业务线程 10)BIO 模式下 最小线闲置线程数

线程模型

BIO线程模型

image.png

NIO线程模型

image.png

Tomcat性能压测

我们先写一个简单的接口代码如下

@GetMapping("/hello")
public String getList() throws InterruptedException {
    //模拟业务请求处理0.6秒
    Thread.sleep(600);
    return "hello world";
}

使用jemter工具进行压测,不做任何配置修改,使用SpringBoot的默认配置

根据上面介绍的内容可知,默认有200个最大线程数,我们压测200的并发查看结果

为了保证结果的准确性,先对刚启动的项目做1分钟的预热

image.png 查看聚合报告可以发现,200并发下错误率为零最大响应时间也只有723完全没问题

接下来修改并发数为201查看压测结果,先看执行一次的结果

image.png 通过上图可以看到当并发数超过了最大线程数时,最大响应时间已经到了1058,错误率也达到了0.5%,这错误率就是多出来的1个并发数需要等待空闲线程执行。

修改tomcat配置参数如下所示

server:
  port: 8082
  servlet:
    context-path: /
  tomcat:
    accept-count: 100 #排队连接数
    # 连接超时时间
    connection-timeout: 1000
    # 最大连接数,可以适应 APR 模式
    max-connections: 8192
    max-threads: 800
#4核8G内存,线程数经验值为800,操作系统之间做线程之间切换调度是有开销的,不是越多越好

再进行一轮压测查看结果

image.png 从上图可以看出设置了800的最大线程数后201并发已经没有异常情况出现,此时如果有801个线程进来又该怎么办。这时候如果在提高最大线程数对性能的提示已经变得微乎其微了,因为,增加线程是有成本的,更多的线程,不仅仅会带来更多的线程上下文切换成本,而且意味着带来更多的内存消耗。JVM中默认情况下在创建新线程时会分配大小为1M的线程栈,所以,更多的线程意味着需要更多的内存。线程数的经验值为:1核2g内存,线程数经验值200;4核8g内存,线程数经验值800。