0


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

  1. Spring 生态的重要性不用多说,Spring Boot 已经成为 Java 后端开发的"标准",但是一个Spring Boot 项目到底能同时应对多少请求呢?你有没有考虑过这个问题呢?这时你可能会问,处理的业务是什么?服务的配置是什么样的?使用的 WEB 容器是什么等等问题,当然我们说的是默认配置,即什么也不配置的情况下到底能应对多少并发请求?
  2. 下面我们通过项目演示逐步深入到源码内部,带你去揭开谜底,首先是项目搭建。

一、项目搭建

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

  1. 添加依赖
  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-web</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.projectlombok</groupId>
  8. <artifactId>lombok</artifactId>
  9. </dependency>
  10. <dependency>
  11. <groupId>cn.hutool</groupId>
  12. <artifactId>hutool-all</artifactId>
  13. <version>5.4.0</version>
  14. </dependency>
  15. </dependencies>
  1. 测试接口代码
  1. @Slf4j
  2. @RestController
  3. public class TestController {
  4. @GetMapping("/testRequest")
  5. public void test(int num) throws InterruptedException {
  6. log.info("{} 接受到请求:num={}", Thread.currentThread().getName(), num);
  7. // 这里让线程睡眠时间长,保证会阻塞住,方便验证
  8. TimeUnit.HOURS.sleep(1);
  9. }
  10. }
  1. 下面在来看调试代码,当然你可以使用 Jmeter 等压测工具,这样显的高达上,这里我们使用一个循环不断调用测试接口,具体代码如下:
  1. public class RequestTest {
  2. public static void main(String[] args) {
  3. for (int i = 0; i < 1000; i++) {
  4. int finalI = i;
  5. new Thread(() -> {
  6. HttpUtil.get("http://127.0.0.1:8080/testRequest?num=" + finalI);
  7. }).start();
  8. }
  9. // 阻塞主线程
  10. Thread.yield();
  11. }
  12. }
  1. 注意,我们的 application.properties 配置文件中什么内容也没有添加。

二、验证最大并发数

  1. 首先启动项目,让服务跑起来,然后运行 RequestTest.main,这时我们的接口就会收到大量的请求,具体如下:![](https://i-blog.csdnimg.cn/blog_migrate/5a6ed75b1f00c6ed1c6dbb3483795236.png)
  2. 接下来,我们直接统计打印的日志,就能统计出处理了多少并发请求。通过下图可以看到,默认配置的情况下,可以接受200个并发请求,在多的请求都被拒绝了,也无法处理。

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

三、源码分析

  1. 首先明确下这 200 线程是谁的线程,那还用说吗,肯定是 Tomcat 的线程。那说明 Tomcat 最大核心线程数是 200 个,那这个值是如何设置的呢?
  2. 我们知道 Spring Boot 最大的特性是自动装配,那 Spring Boot 启动时内嵌的 Tomcat 参数是如何设置的,下面看下自动装配的具体实现。
  3. spring-boot-autoconfigure spring.factories 文件中有很多自动装配的类,其中一项org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,就是通过 EmbeddedWebServerFactoryCustomizerAutoConfiguration 自动装配类来完成配置的。

  1. 在来具体看下 EmbeddedWebServerFactoryCustomizerAutoConfiguration 的实现。
  1. @Configuration(proxyBeanMethods = false)
  2. @ConditionalOnWebApplication
  3. @EnableConfigurationProperties(ServerProperties.class)
  4. public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {
  5. /**
  6. * Nested configuration if Tomcat is being used.
  7. */
  8. @Configuration(proxyBeanMethods = false)
  9. @ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class })
  10. public static class TomcatWebServerFactoryCustomizerConfiguration {
  11. @Bean
  12. public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment,
  13. ServerProperties serverProperties) {
  14. return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
  15. }
  16. }
  17. // 其他源码省略
  18. }
  1. 其中将 ServerProperties 传入了 tomcatWebServerFactoryCustomizer 方法中,最后返回了TomcatWebServerFactoryCustomizer,在 ServerProperties 的实现中设置了最大线程数,然后在 TomcatWebServerFactoryCustomizer 中进行了设置,具体如下图标识。

  1. 到这里我们就回答了为什么在默认情况下 Spring Boot 能处理的最大并发数是 200 了,也就是你在配置文件中通过 server.tomcat.max-threads 配置项可以修改最大并发数的原因所在。
  2. 到这里就结束了吗?no,我们继续来看下其他核心配置,来进一步掌握更多的细节。

3.1 Tomcat 核心配置讲解

  1. 通过 ServerProperties 我们发现 Tomcat 还有很多其他的配置,我们来分别介绍下,具体的源码细节与刚才一样,自己找就可以,这里放上部分代码:
  1. /**
  2. * Tomcat properties.
  3. */
  4. public static class Tomcat {
  5. /**
  6. * Access log configuration.
  7. */
  8. private final Accesslog accesslog = new Accesslog();
  9. /**
  10. * Regular expression that matches proxies that are to be trusted.
  11. */
  12. private String internalProxies = "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 10/8
  13. + "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" // 192.168/16
  14. + "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" // 169.254/16
  15. + "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 127/8
  16. + "172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" // 172.16/12
  17. + "172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}|" //
  18. + "0:0:0:0:0:0:0:1|::1";
  19. /**
  20. * Header that holds the incoming protocol, usually named "X-Forwarded-Proto".
  21. */
  22. private String protocolHeader;
  23. /**
  24. * Value of the protocol header indicating whether the incoming request uses SSL.
  25. */
  26. private String protocolHeaderHttpsValue = "https";
  27. /**
  28. * Name of the HTTP header used to override the original port value.
  29. */
  30. private String portHeader = "X-Forwarded-Port";
  31. /**
  32. * Name of the HTTP header from which the remote IP is extracted. For instance,
  33. * `X-FORWARDED-FOR`.
  34. */
  35. private String remoteIpHeader;
  36. /**
  37. * Name of the HTTP header from which the remote host is extracted.
  38. */
  39. private String hostHeader = "X-Forwarded-Host";
  40. /**
  41. * Tomcat base directory. If not specified, a temporary directory is used.
  42. */
  43. private File basedir;
  44. /**
  45. * Delay between the invocation of backgroundProcess methods. If a duration suffix
  46. * is not specified, seconds will be used.
  47. */
  48. @DurationUnit(ChronoUnit.SECONDS)
  49. private Duration backgroundProcessorDelay = Duration.ofSeconds(10);
  50. /**
  51. * Maximum amount of worker threads.
  52. */
  53. private int maxThreads = 200;
  54. /**
  55. * Minimum amount of worker threads.
  56. */
  57. private int minSpareThreads = 10;
  58. /**
  59. * Maximum size of the form content in any HTTP post request.
  60. */
  61. private DataSize maxHttpFormPostSize = DataSize.ofMegabytes(2);
  62. /**
  63. * Maximum amount of request body to swallow.
  64. */
  65. private DataSize maxSwallowSize = DataSize.ofMegabytes(2);
  66. /**
  67. * Whether requests to the context root should be redirected by appending a / to
  68. * the path.
  69. */
  70. private Boolean redirectContextRoot = true;
  71. /**
  72. * Whether HTTP 1.1 and later location headers generated by a call to sendRedirect
  73. * will use relative or absolute redirects.
  74. */
  75. private Boolean useRelativeRedirects;
  76. /**
  77. * Character encoding to use to decode the URI.
  78. */
  79. private Charset uriEncoding = StandardCharsets.UTF_8;
  80. /**
  81. * Maximum number of connections that the server accepts and processes at any
  82. * given time. Once the limit has been reached, the operating system may still
  83. * accept connections based on the "acceptCount" property.
  84. */
  85. private int maxConnections = 10000;
  86. /**
  87. * Maximum queue length for incoming connection requests when all possible request
  88. * processing threads are in use.
  89. */
  90. private int acceptCount = 100;
  91. /**
  92. * Maximum number of idle processors that will be retained in the cache and reused
  93. * with a subsequent request. When set to -1 the cache will be unlimited with a
  94. * theoretical maximum size equal to the maximum number of connections.
  95. */
  96. private int processorCache = 200;
  97. /**
  98. * Comma-separated list of additional patterns that match jars to ignore for TLD
  99. * scanning. The special '?' and '*' characters can be used in the pattern to
  100. * match one and only one character and zero or more characters respectively.
  101. */
  102. private List<String> additionalTldSkipPatterns = new ArrayList<>();
  103. /**
  104. * Comma-separated list of additional unencoded characters that should be allowed
  105. * in URI paths. Only "< > [ \ ] ^ ` { | }" are allowed.
  106. */
  107. private List<Character> relaxedPathChars = new ArrayList<>();
  108. /**
  109. * Comma-separated list of additional unencoded characters that should be allowed
  110. * in URI query strings. Only "< > [ \ ] ^ ` { | }" are allowed.
  111. */
  112. private List<Character> relaxedQueryChars = new ArrayList<>();
  113. /**
  114. * Amount of time the connector will wait, after accepting a connection, for the
  115. * request URI line to be presented.
  116. */
  117. private Duration connectionTimeout;
  118. /**
  119. * Static resource configuration.
  120. */
  121. private final Resource resource = new Resource();
  122. /**
  123. * Modeler MBean Registry configuration.
  124. */
  125. private final Mbeanregistry mbeanregistry = new Mbeanregistry();
  126. // 省略代码
  127. }
  • server.tomcat.min-spare-threads:设定 Tomcat 在启动时创建的最小空闲线程数,即使没有请求到来,也会维持这么多线程空闲等待。其默认值为 10

  • server.tomcat.accept-count:当所有请求处理线程都忙时,可以排队等待的最大请求数量。超过这个数量的请求将被拒绝。其默认值为 100

  • server.tomcat.max-connections:Tomcat能够同时处理的最大连接数,包括正在处理的请求和等待队列中的请求,其默认值为:10000

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

往期经典推荐

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

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

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

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

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

标签: spring boot 后端 java

本文转载自: https://blog.csdn.net/qq_39209927/article/details/136428037
版权归原作者 超越不平凡 所有, 如有侵权,请联系我们删除。

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

还没有评论