SpringBoot可以同时处理多少请求?

142 阅读5分钟

前言

前两天我打开 B 站,机缘巧合下,点进了鱼皮大佬的直播间,正在进行模拟面试,听到一个问题。

大概在问:Redis能承受多大的访问量?

这个问题我确实不知道,后面会补充上,但是,让我好奇的是,从学习Java的路线来说,应该先接触 SpringBoot ,那 SpringBoot 能同时处理多少请求?

然后,我就在网上 Google 搜索了一番,看到的解释都是云里雾里的,说实话不太明白,只看到了可以同时处理 100+8192个请求,至于是为什么着实没看懂。

还是最近在逛 V 站的时候,看到一个问题: 求好用的中文搜索引擎

然后,我当天就收藏了我认为好用的搜索引擎 大同搜索,我就带着试一试的心态,没想到效果很好,搜索结果的第二个就是我要找的答案,因为第一个解释的我没看懂,哈哈。

大同搜索我了解的不多,就知道它是目前最好用的,国内的,基于 AI 的搜索引擎。

好的,那么就直接进入今天的重点。

正文

我们都知道,SpringBoot默认的内嵌容器是Tomcat,也就是我们的程序实际上是运行在Tomcat里的。所以与其说SpringBoot可以处理多少请求,到不如说Tomcat可以处理多少请求。

关于Tomcat的默认配置,都在spring-configuration-metadata.json文件中,对应的配置类则是org.springframework.boot.autoconfigure.web.ServerProperties

【注】:我用的SpringBoot版本是2.7.18,在spring-configuration-metadata.json文件中没有找到关于 tomcat 相关的配置参数(我就是搜了一下),我在org.springframework.boot.autoconfigure.web.ServerProperties这里面找到了 tomcat 配置的相关内容。

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
    public static class Tomcat {

            // 上面源码省略...

            /**
             * 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.
             */
            private int maxConnections = 8192;

            /**
             * Maximum queue length for incoming connection requests when all possible request
             * processing threads are in use.
             */
            private int acceptCount = 100;

            // 中间源码省略...

            /**
             * Tomcat thread properties.
             */
            public static class Threads {

                /**
                 * Maximum amount of worker threads.
                 */
                private int max = 200;

                /**
                 * Minimum amount of worker threads.
                 */
                private int minSpare = 10;
            }
    }
}

server.tomcat.max-connections:最大连接数,默认大小是8192。表示Tomcat可以处理的最大请求数量,超过8192的请求就会被放入到等待队列。 server.tomcat.accept-count:等待队列的长度,默认大小是100。

server.tomcat.threads.max:最多的工作线程数,默认大小是200。该参数相当于临时工,如果并发请求的数量在10到200之间,就会使用这些临时工线程进行处理。

server.tomcat.threads.min-spare:最少的工作线程数,默认大小是10。该参数相当于长期工,如果并发请求的数量达不到10,就会依次使用这几个线程去处理请求。

举个例子说明一下这几个参数之间的关系:

图来自 Robod,画的太好了,我很难画出比他画的还形象的图

image.png

如果把 Tomcat 比作一家饭店的话,那么一个请求其实就相当于一位客人。min-spare 就是厨师(长期工);max 是厨师总数(长期工+临时工,这点需要注意);max-connections 就是饭店里的座位数量;accept-count 是门口小板凳的数量。来的客人优先坐到饭店里面,然后厨师开始忙活,如果长期工可以干的完,就让长期工干,如果长期工干不完,就再让临时工干。图中画的厨师一共15人,饭店里有30个座位,也就是说,如果现在来了20个客人,那么就会有5个人先在饭店里等着。如果现在来了35个人,饭店里坐不下,就会让5个人先到门口坐一下。如果来了50个人,那么饭店座位+门口小板凳一共40个,所以就会有10人离开。

也就是说,SpringBoot 同时所能处理的最大请求数量是 max-connections + accept-count,超过该数量的请求直接就会被丢掉。

纸上得来终觉浅,绝知此事要躬行。

上面只是理论结果,现在通过一个实际的小例子来演示一下到底是不是这样:

创建一个 SpringBoot 的项目,在 application.yml 里配置一下这几个参数,因为默认的数量太大,不好测试,所以配小一点:

server:
  tomcat:
    threads:
      # 最少线程数
      min-spare: 10
      # 最多线程数
      max: 15
    # 最大连接数
    max-connections: 30
    # 最大等待数
    accept-count: 10

再来写一个简单的接口:

@GetMapping("/test")
public String test(HttpServletRequest request) throws InterruptedException {
    log.info("ip:{},线程:{}", request.getRemoteAddr(), Thread.currentThread().getName());
    Thread.sleep(500);
    return "ok";
}

代码很简单,只是打印了一下线程名,然后休眠 0.5 秒,这样肯定会导致部分请求处理一次性处理不了而进入到等待队列。

然后我用 Apifox (第一次使用 apifox 进行压测,之前了解的都是 jmeter,教程:使用apifox进行接口并发测试)创建了一个测试用例,去模拟 100 个请求:

image.png

观察一下测试结果:

image.png

从结果中可以看出,由于设置的 max-connections + accept-count 的和是 40,所以有 60 个请求会被丢弃,这和我们的预期是相符的。由于最大线程是 15,也就是有 25 个请求会先等待,等前 15 个处理完了再处理 15 个,最后在处理 10 个,也就是将 40 个请求分成了 15,15,10 这样三批进行处理。

image.png

再从控制台的打印日志可以看到,线程的最大编号是 15,这也印证了前面的想法。

总结一下:如果并发请求数量低于server.tomcat.threads.max,则会被立即处理,超过的部分会先进行等待,如果数量超过 max-connections 与 accept-count 之和,则多余的部分则会被直接丢弃。

参考文章