SpringBoot以其简洁高效的开发方式和强大的内嵌容器特性,为开发者提供了构建高性能后端服务的便利。然而,当面临高并发场景时,理解并合理配置Spring Boot应用以达到最佳的并发处理能力至关重要。在Spring Boot中,应用程序对HTTP请求的并发处理主要依赖于内嵌的Servlet容器(如Tomcat)。接下来我们以Tomcat为例谈谈这个问题。

本文以SpringBoot2.7.0为例。

在接下来讲解Tomcat工作线程池之前,你可以先了解一下Java线程池的工作原理,请参考这篇文章:Java线程池最全详解

请求处理

我们的程序实际上是运行在Tomcat里的。所以SpringBoot可以处理多少请求,其实也就是看Tomcat可以处理多少请求。Tomcat的工作线程池负责接收并处理这些请求。在SpringBoot中关于Tomcat工作项城池的参数可以在spring-boot-autoconfigure的依赖包中的spring-configuration-metadata.json中有如下四个参数控制Tomcat的工作线程池:

{
	{  
	  "name": "server.tomcat.threads.max",  
	  "type": "java.lang.Integer",  
	  "description": "Maximum amount of worker threads.",  
	  "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties$Tomcat$Threads",  
	  "defaultValue": 200  
	},  
	{  
	  "name": "server.tomcat.threads.min-spare",  
	  "type": "java.lang.Integer",  
	  "description": "Minimum amount of worker threads.",  
	  "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties$Tomcat$Threads",  
	  "defaultValue": 10  
	},
	{  
	  "name": "server.tomcat.max-connections",  
	  "type": "java.lang.Integer",  
	  "description": "Maximum number of connections that the server accepts and processes at any given time. Once the limit has been reached, the operating system may still accept connections based on the \"acceptCount\" property.",  
	  "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties$Tomcat",  
	  "defaultValue": 8192  
	},
	{  
	  "name": "server.tomcat.accept-count",  
	  "type": "java.lang.Integer",  
	  "description": "Maximum queue length for incoming connection requests when all possible request processing threads are in use.",  
	  "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties$Tomcat",  
	  "defaultValue": 100  
	}
}

我们来看一下这四个参数的含义:

  • server.tomcat.threads.max:用于配置Tomcat内嵌服务器的工作线程池的最大工作线程数。工作线程用于处理接收到的HTTP请求,处理完毕后将返回到线程池中等待下一个请求。当并发请求数量小于等于 threads.max 时,Tomcat尽可能使用现有的工作线程来处理请求,如果并发请求数量超过了 threads.maxTomcat不再创建新的工作线程,而是将请求放入队列等待。该值可以看做是一个临时工,如果并发请求的数量在threads.min-sparethreads.max之间,就会使用这些临时工线程进行处理。默认值为200。主要关注的是工作线程池,用于处理请求的线程数。适用于调整并发请求的处理能力。

  • server.tomcat.threads.min-spare:确定了工作线程池的最小空闲线程数量,即使没有请求需要处理,也会保持这些线程处于等待状态,以便快速响应新的请求。该值可以看做是一个长期工,如果并发请求数小于min-spare,就会使用这些线程去处理请求。默认值是10,即使没有活跃的请求,Tomcat也会保持至少 10 个空闲线程。

  • server.tomcat.max-connections:用于配置Tomcat内嵌服务器的连接池的最大连接数。即允许同时存在的最大连接数。这个连接数是针对整个连接池的,不仅仅是工作线程的数量。当并发请求数量导致服务器需要创建新的连接时,Tomcat 会检查连接池中的连接数是否已经达到了 max-connections 配置的最大值。如果已经达到最大值,新的连接请求将被放入等待队列中,直到有连接被释放。默认值8192。

  • server.tomcat.accept-count:用于配置Tomcat连接器所允许的最大等待接受连接的请求数(即等待队列)。当所有线程都在处理其他请求时,新的连接请求将会排队等待。如果队列已满,额外的连接将被拒绝。默认值是100。

我们以饭馆的示例去说明这几个参数之间的关系:
我们把Tomcat比作一家热门的餐厅,每个顾客都是一个请求。餐厅里一直有一部分厨师一直待命,无论有没有顾客,确保随时能够处理点单。这部分厨师就是min-spare(长期工),当餐厅遇到一些大的宴会时,餐厅老板就会从外面找过来一批厨师帮忙,这部分厨师就是临时工,临时工+长期工就是(max)。餐厅的座位就表示同时最大可以容纳啊就餐的顾客人数,即max-connections。假如某时餐厅生意火爆,餐厅的座位都坐满以后,此时餐厅就会想办法在餐厅门口放上一排座椅,可以让后来的顾客在这里等待,假如里面有顾客吃完,这时外面等待的顾客就可以进去用餐。如果餐厅门口的座椅也做满,再来的顾客就会离开。

假如上述就是那家餐厅,2个绿色框代表长期工厨师即min-spare,3个红色框代表临时工厨师,共有5名厨师,即max数量,5个棕色的队列即餐厅椅子数量,即5个max-connections,餐厅最多可以容忍5名顾客吃饭,2个橘色队列代表门口等待区椅子数量,即2个accept-count。假如此时来了4个客人,那就可以区餐厅吃饭,如果来了7个客人,那么有5个客人进去吃饭,2个客人在门口等待,如果来了8个客人,那么有5个客人进去吃饭,2个客人在门口等待,剩下1个客人就会直接离开。

示例

我们创建一个SpringBoot项目,我们将上述四个参数分别修改为如下:

server.tomcat.accept-count=2  
server.tomcat.max-connections=5  
server.tomcat.threads.max=5  
server.tomcat.threads.min-spare=2

然后我们在定义一个接口,用于模拟客户端请求:

@RestController  
@RequestMapping("test")  
public class TestMaxRequestController {  
  
    @GetMapping("maxRequest")  
    @ResponseBody  
    public String testMaxRequest() throws InterruptedException {  
        System.out.println("当前请求线程: "+Thread.currentThread().getName());  
        Thread.sleep(2000);  
        return "Hello World";  
    }  
}

我们启动项目,使用JVisualVM监测一下此时项目中的线程数为2个:

image.png

此时我们使用Apifox发起一个请求,观察线程数没有增长。我们在使用Apifox中模拟3个线程同时请求,发现此时线程数变成5个:
image.png

我们继续增加线程数到7个,我们发现线程数还是5个,因最大线程数等于5个:

image.png

后台请求全部成功:
image.png

我们继续增加线程数到8个,此时线城数依然是5个,设置请求超时时间为1秒,但是有一个请求失败:

image.png

阿里二面:SpringBoot可以同时处理多少个请求?当场懵了。。。。-小白菜博客
image.png

服务端只处理了7个请求:
image.png

结论

SpringBoot可以同时处理多少个请求,需要看其内嵌的Sevlet处理线程的能力,以Tomcat为例,即Tomcat可以处理多少请求,这需要看配置文件中设置的server.tomcat.accept-count以及server.tomcat.max-connections的大小。Tomcat同时可以处理的请求为server.tomcat.accept-count加上 server.tomcat.max-connections。实际生产应用中我们需要根据服务器,硬件的配置等综合因素考虑后,调节这两个值,确保应用程序能够处理预期的并发量。

本文已收录于我的个人博客:码农Academy的博客,专注分享Java技术干货,包括Java基础、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中间件、架构设计、面试题、程序员攻略等