SpringBoot项目并发处理大揭秘,你知道它到底能应对多少请求洪峰?

本文通过实践演示和源码解析,揭示了SpringBoot项目在默认配置下的最大并发数限制,重点关注了Tomcat的核心配置和如何通过`server.tomcat.max-threads`进行调整。
摘要由CSDN通过智能技术生成

目录

一、项目搭建

二、验证最大并发数

三、源码分析

        3.1 Tomcat 核心配置讲解


        Spring 生态的重要性不用多说,Spring Boot 已经成为 Java 后端开发的"标准",但是一个Spring Boot 项目到底能同时应对多少请求呢?你有没有考虑过这个问题呢?这时你可能会问,处理的业务是什么?服务的配置是什么样的?使用的 WEB 容器是什么等等问题,当然我们说的是默认配置,即什么也不配置的情况下到底能应对多少并发请求?

        下面我们通过项目演示逐步深入到源码内部,带你去揭开谜底,首先是项目搭建。

一、项目搭建

        项目结构很简单,三个类,一个配置文件,其中一个还是启动类,使用的Spring Boot 版本是 2.2.1.RELEASE。

        添加依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.4.0</version>
    </dependency>
</dependencies>

        测试接口代码

@Slf4j
@RestController
public class TestController {

    @GetMapping("/testRequest")
    public void test(int num) throws InterruptedException {
        log.info("{} 接受到请求:num={}", Thread.currentThread().getName(), num);
        // 这里让线程睡眠时间长,保证会阻塞住,方便验证
        TimeUnit.HOURS.sleep(1);
    }
}

        下面在来看调试代码,当然你可以使用 Jmeter 等压测工具,这样显的高达上,这里我们使用一个循环不断调用测试接口,具体代码如下:

public class RequestTest {

    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            int finalI = i;
            new Thread(() -> {
                HttpUtil.get("http://127.0.0.1:8080/testRequest?num=" + finalI);
            }).start();
        }
        // 阻塞主线程
        Thread.yield();
    }
}

        注意,我们的 application.properties 配置文件中什么内容也没有添加。

二、验证最大并发数

        首先启动项目,让服务跑起来,然后运行 RequestTest.main,这时我们的接口就会收到大量的请求,具体如下:

        接下来,我们直接统计打印的日志,就能统计出处理了多少并发请求。通过下图可以看到,默认配置的情况下,可以接受200个并发请求,在多的请求都被拒绝了,也无法处理。

        于是这时可以得出答案,一个 Spring Boot 项目在默认配置下,最多可以处理200个并发请求,下面来看看这个是怎么来的?

三、源码分析

        首先明确下这 200个 线程是谁的线程,那还用说吗,肯定是 Tomcat 的线程。那说明 Tomcat 最大核心线程数是 200 个,那这个值是如何设置的呢?

        我们知道 Spring Boot 最大的特性是自动装配,那 Spring Boot 启动时内嵌的 Tomcat 参数是如何设置的,下面看下自动装配的具体实现。

        在 spring-boot-autoconfigure 的 spring.factories 文件中有很多自动装配的类,其中一项org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,就是通过 EmbeddedWebServerFactoryCustomizerAutoConfiguration 自动装配类来完成配置的。

        在来具体看下 EmbeddedWebServerFactoryCustomizerAutoConfiguration 的实现。

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication
@EnableConfigurationProperties(ServerProperties.class)
public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {

    /**
     * Nested configuration if Tomcat is being used.
     */
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class })
    public static class TomcatWebServerFactoryCustomizerConfiguration {

        @Bean
        public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment,
                ServerProperties serverProperties) {
            return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
        }

    }


    // 其他源码省略


}

        其中将 ServerProperties 传入了 tomcatWebServerFactoryCustomizer 方法中,最后返回了TomcatWebServerFactoryCustomizer,在 ServerProperties 的实现中设置了最大线程数,然后在 TomcatWebServerFactoryCustomizer 中进行了设置,具体如下图标识。

        到这里我们就回答了为什么在默认情况下 Spring Boot 能处理的最大并发数是 200 了,也就是你在配置文件中通过 server.tomcat.max-threads 配置项可以修改最大并发数的原因所在。

        到这里就结束了吗?no,我们继续来看下其他核心配置,来进一步掌握更多的细节。

        3.1 Tomcat 核心配置讲解

        通过 ServerProperties 我们发现 Tomcat 还有很多其他的配置,我们来分别介绍下,具体的源码细节与刚才一样,自己找就可以,这里放上部分代码:

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

        /**
         * Access log configuration.
         */
        private final Accesslog accesslog = new Accesslog();

        /**
         * Regular expression that matches proxies that are to be trusted.
         */
        private String internalProxies = "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 10/8
                + "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" // 192.168/16
                + "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" // 169.254/16
                + "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 127/8
                + "172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" // 172.16/12
                + "172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}|" //
                + "0:0:0:0:0:0:0:1|::1";

        /**
         * Header that holds the incoming protocol, usually named "X-Forwarded-Proto".
         */
        private String protocolHeader;

        /**
         * Value of the protocol header indicating whether the incoming request uses SSL.
         */
        private String protocolHeaderHttpsValue = "https";

        /**
         * Name of the HTTP header used to override the original port value.
         */
        private String portHeader = "X-Forwarded-Port";

        /**
         * Name of the HTTP header from which the remote IP is extracted. For instance,
         * `X-FORWARDED-FOR`.
         */
        private String remoteIpHeader;

        /**
         * Name of the HTTP header from which the remote host is extracted.
         */
        private String hostHeader = "X-Forwarded-Host";

        /**
         * Tomcat base directory. If not specified, a temporary directory is used.
         */
        private File basedir;

        /**
         * Delay between the invocation of backgroundProcess methods. If a duration suffix
         * is not specified, seconds will be used.
         */
        @DurationUnit(ChronoUnit.SECONDS)
        private Duration backgroundProcessorDelay = Duration.ofSeconds(10);

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

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

        /**
         * Maximum size of the form content in any HTTP post request.
         */
        private DataSize maxHttpFormPostSize = DataSize.ofMegabytes(2);

        /**
         * Maximum amount of request body to swallow.
         */
        private DataSize maxSwallowSize = DataSize.ofMegabytes(2);

        /**
         * Whether requests to the context root should be redirected by appending a / to
         * the path.
         */
        private Boolean redirectContextRoot = true;

        /**
         * Whether HTTP 1.1 and later location headers generated by a call to sendRedirect
         * will use relative or absolute redirects.
         */
        private Boolean useRelativeRedirects;

        /**
         * Character encoding to use to decode the URI.
         */
        private Charset uriEncoding = StandardCharsets.UTF_8;

        /**
         * 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 = 10000;

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

        /**
         * Maximum number of idle processors that will be retained in the cache and reused
         * with a subsequent request. When set to -1 the cache will be unlimited with a
         * theoretical maximum size equal to the maximum number of connections.
         */
        private int processorCache = 200;

        /**
         * Comma-separated list of additional patterns that match jars to ignore for TLD
         * scanning. The special '?' and '*' characters can be used in the pattern to
         * match one and only one character and zero or more characters respectively.
         */
        private List<String> additionalTldSkipPatterns = new ArrayList<>();

        /**
         * Comma-separated list of additional unencoded characters that should be allowed
         * in URI paths. Only "< > [ \ ] ^ ` { | }" are allowed.
         */
        private List<Character> relaxedPathChars = new ArrayList<>();

        /**
         * Comma-separated list of additional unencoded characters that should be allowed
         * in URI query strings. Only "< > [ \ ] ^ ` { | }" are allowed.
         */
        private List<Character> relaxedQueryChars = new ArrayList<>();

        /**
         * Amount of time the connector will wait, after accepting a connection, for the
         * request URI line to be presented.
         */
        private Duration connectionTimeout;

        /**
         * Static resource configuration.
         */
        private final Resource resource = new Resource();

        /**
         * Modeler MBean Registry configuration.
         */
        private final Mbeanregistry mbeanregistry = new Mbeanregistry();

        // 省略代码

    }
  • server.tomcat.min-spare-threads:设定 Tomcat 在启动时创建的最小空闲线程数,即使没有请求到来,也会维持这么多线程空闲等待。其默认值为 10
  • server.tomcat.accept-count:当所有请求处理线程都忙时,可以排队等待的最大请求数量。超过这个数量的请求将被拒绝。其默认值为 100
  • server.tomcat.max-connections:Tomcat能够同时处理的最大连接数,包括正在处理的请求和等待队列中的请求,其默认值为:10000

        一些关键参数就介绍到这里,大家下来可以再自己的代码中配置修改这些值,然后验证一下。其他参数看注释就可以了,欢迎留言讨论。

往期经典推荐

实战分享:Tomcat打破双亲委派模型,实现Web应用独立与安全隔离的奥秘-CSDN博客

你所不知的Tomcat网络通信的玄机-CSDN博客

Raft领导者选举你真的了解了?-CSDN博客

Kafka消息流转的挑战与对策:消息丢失与重复消费问题_kafka发送消息生产者关闭了-CSDN博客

决胜高并发战场:Redis并发访问控制与实战解析-CSDN博客

  • 44
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: Spring Boot一个基于Spring框架的开源Java Web框架,它本身并不限制并发,而是取决于服务器的硬件配置、网络带宽和具体业务场景。 在正式回答能够承受多少并发之前,需要先了解并发的定义和计算方式。并发是指在同一时间内向服务器发起请求量,通常使用QPS(每秒查询率)或TPS(每秒事务)来衡量并发量的大小。 在合理的服务器硬件配置和优化的情况下,Spring Boot应用程序通常可以承受百甚至千的并发请求。但是,具体的并发承载能力取决于多个因素,如服务器的硬件配置、网络带宽、据库性能、应用程序设计和优化等因素。因此,需要根据具体业务场景和实际测试结果来确定应用程序的并发承载能力。 ### 回答2: Spring Boot一个用于构建Java应用程序的开源框架。它内置了许多用于提高性能和并发能力的功能。但是,确定一个Spring Boot项目可以承受多少并发是很困难的,因为它受到许多因素的影响。以下是一些影响并发能力的主要因素: 1. 硬件资源:服务器的处理能力、内存大小、网络带宽等硬件资源直接影响着项目的承受并发能力。更强大的硬件资源可以支持更多的并发请求。 2. 代码质量:代码质量和设计对项目并发能力有直接影响。如果代码没有优化或存在性能问题,那么项目可能无法很好地处理并发请求。 3. 据库和缓存:据库和缓存的性能也是影响并发能力的因素之一。如果据库或缓存无法处理大量并发请求,那么项目并发能力将受到限制。 4. 配置调整:通过调整项目的配置参,如线程池大小和连接池大小,可以改善项目并发能力。适当的配置调整可以提高项目的吞吐量和响应能力。 总之,确定一个Spring Boot项目可以承受多少并发需要考虑许多因素,并且很难给出一个具体的字。可以通过合理优化代码、增加硬件资源、调整配置参等措施来提高项目并发能力。 ### 回答3: Spring Boot一个Java开发框架,它本身并不限制项目能够承受的并发项目并发能力主要取决于服务器的硬件性能和配置、网络带宽以及项目本身的设计和实现。 首先,服务器的硬件性能对项目并发能力有重要影响。高性能的处理器、足够的内存和高速的硬盘都能提升服务器的并发处理能力。合理的硬件配置可以支持更多的并发请求。 其次,网络带宽也是影响并发能力的关键因素。有足够宽带的网络连接能够更快地传输据,进而提高项目并发处理能力。如果网络带宽较低,可能会限制项目并发。 最后,项目本身的设计和实现对并发能力也有影响。合理的架构设计和精细的代码实现可以提高项目并发处理效率。使用Spring Boot的异步特性、线程池等措施,可以提高项目并发处理能力。 综上所述,Spring Boot项目并发能力取决于服务器硬件性能、网络带宽以及项目本身的设计和实现。没有一个准确的字可以表示Spring Boot项目能够承受多少并发,它可以根据具体的情况进行优化并提高并发能力。为了提高项目并发处理能力,可以通过升级硬件、增加带宽、优化代码等手段来实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

超越不平凡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值