添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

SpringBoot开发最大的好处是简化配置,内置了Tomcat, 在SpringBoot2.0.x版本中内置Tomcat版本是8.5.x,SpringBoot内置Tomcat的默认设置中,Tomcat的等待队列长度默认是100,Tomcat的最小工作线程数默认分配10,Tomcat的最大线程数是200,最大连接数是10000,至于最大并发量和最大连接数,常常理解成最大并发量就是最大连接数,实际上是有些牵强的,最大连接数并不一定就是最大并发量。

SpringBoot内置Tomcat的包重要配置和类在

package org.springframework.boot.autoconfigure.web;

找到ServerProperties中的public static class Tomcat对象

package org.springframework.boot.autoconfigure.web;
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
         * Maximum size, in bytes, of the HTTP post content.
        private int maxHttpPostSize = 2097152;
         * Maximum size, in bytes, of the HTTP message header.
        private int maxHttpHeaderSize = 0;
         * 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;
 

默认最大连接数maxConnections = 10000

默认队列长度acceptCount = 100

默认最大工作线程数maxThreads = 200

默认最小工作线程数 minSpareThreads = 10

也就是说配置如下

server.tomcat.accept-count = 100 server.tomcat.max-connections = 10000 server.tomcat.max-threads = 200 server.tomcat.min-spare-threads=10

在这里有个点儿

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 服务器在任何给定时间接受和处理的最大连接数。一旦达到限制,操作系统仍然可以根据“acceptCount”属性接受连接

也就是说当服务器已经达到最大连接数后,操作系统任然可以根据队列长度来接收连接

有时候我们通常会认为在默认配置下,最大并发量就是最大连接数,超过最大连接数10000后会出现tomcat拒绝连接的情况,触发的请求任务超过默认值200(最大线程数)+默认值100(等待队列长度)后,tomcat会拒绝处理请求任务

最大并发量,每个人都它的理解是不一样的

  1. 如果在乎tomcat运行能够同时处理的任务数量,那最大并发量可能理解成最大工作线程数(max-threads)---不包含队列里的数量(acceptCount)

  1. 如果在乎tomcat运行能够接纳的最大最多的任务数量,那最大并发量可以理解成最大连接数(max-connections)+队列长度的数量(accept-count) --- 包含队列里的数量(acceptCount)

通常对SpringBoot内置Tomcat调优主要是针对最大连接数(maxConnections = 10000),队列长度(acceptCount = 100),最大工作线程数(maxThreads = 200)和最小工作线程数 (minSpareThreads = 10)按需设置,一般根据服务器的性能(CPU)以及该程序可能面临的业务峰值(IO数据库等操作)进行参考调优。

测试一下SpringBoot内置Tomcat的最小工作线程和最大工作线程以及最大连接数和队列长度

我用的版本是SpringBoot2.0.5

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

简单编写一个http请求的Controller

package boot.example.web.tomcat.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
 *  蚂蚁舞
@Controller
@RequestMapping(value="/demo")
public class BootTomcatController {
    @Resource
    private BootTomcatService bootTomcatService;
    @RequestMapping(value="/test/{count}")
    @ResponseBody
    public String test(@PathVariable(name = "count", required = true) int count) throws InterruptedException {
        bootTomcatService.testTomcatThread(count);
        return "hello world " + count;
 

处理业务的Service

package boot.example.web.tomcat.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
 *  蚂蚁舞
@Service
public class BootTomcatService {
    private final Logger log =  LoggerFactory.getLogger(this.getClass());
    void testTomcatThread(int count) throws InterruptedException {
        log.info(count+"");
        //Thread.sleep(40*1000);
 

配置Tomcat的参数

server.port=8080 server.tomcat.accept-count = 2 server.tomcat.max-connections = 12 server.tomcat.max-threads = 6 server.tomcat.min-spare-threads=3

在这里,我把accept-count配置成2 把max-connections配置成12,把max-threads配置成6,min-spare-threads配置成3

新建一个测试线程

package boot.example.web.tomcat;
import cn.hutool.http.HttpRequest;
 *  蚂蚁舞
public class ThreadTest extends Thread {
    private final int count;
    public ThreadTest(int count){
        this.count = count;
    public void run() {
        try {
            //  设置超时时间很大
            String result = HttpRequest.get("http://127.0.0.1:8080/demo/test/"+count).timeout(10000*1000).execute().body();;
            System.out.println(result);
        } catch (Exception e) {
            throw new RuntimeException(e);
 

这里用到hutool的http请求

<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.5.6</version> </dependency>

启动的main方法

package boot.example.web.tomcat;
public class TestTomcat {
    public static void main(String[] args) {
        for (int i = 1; i < 30; i++) {
            ThreadTest test = new ThreadTest(i);
            test.start();
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            System.out.println(i);
 

启动SpringBoot Tomcat 然后启动测试的main方法 30次请求访问

控制台日志

可以看到他的工作线程是3个,完全是符合server.tomcat.min-spare-threads=3的配置,而且线程数的使用没有达到最大线程数

假如我们把它的工作线程给临时阻塞着,故意让他达到最大线程数,40s时间

    void testTomcatThread(int count) throws InterruptedException {
        log.info(count+"");
        Thread.sleep(40*1000);
 

如此的话运行程序 请求30次

可以看到控制台首先进入了6个请求,阻塞着处理任务,那么配置server.tomcat.max-threads = 6是生效的,等40s之后6个任务线程处理完了业务,接下来又是6个任务线程处理,再等40后还处理了2个任务线程,请求的次数30次 实际处理任务数加起来只有14次

报错信息如下

Exception in thread "Thread-14" java.lang.RuntimeException: cn.hutool.core.io.IORuntimeException: ConnectException: Connection refused: connect
at boot.example.web.tomcat.ThreadTest.run(ThreadTest.java:24)
Caused by: cn.hutool.core.io.IORuntimeException: ConnectException: Connection refused: connect
at cn.hutool.http.HttpRequest.send(HttpRequest.java:1164)
at cn.hutool.http.HttpRequest.execute(HttpRequest.java:969)
at cn.hutool.http.HttpRequest.execute(HttpRequest.java:940)
at boot.example.web.tomcat.ThreadTest.run(ThreadTest.java:21)
Caused by: java.netCaused by: java.net.ConnectException: Connection refused: connect java.netat java.net.DualStackPlainSocketImpl.waitForConnect(Native Method) java.netat java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85) java.netat java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350) java.netat java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206) java.netat java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188) java.netat java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172) java.netat java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) java.netat java.net.Socket.connect(Socket.java:589) sun.netat sun.net.NetworkClient.doConnect(NetworkClient.java:175) sun.netat sun.net.www.http.HttpClient.openServer(HttpClient.java:463) sun.netat sun.net.www.http.HttpClient.openServer(HttpClient.java:558) sun.netat sun.net.www.http.HttpClient.<init>(HttpClient.java:242) sun.netat sun.net.www.http.HttpClient.New(HttpClient.java:339) sun.netat sun.net.www.http.HttpClient.New(HttpClient.java:357) sun.netat sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1220) sun.netat sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1156) sun.netat sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1050) sun.netat sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:984)
at cn.hutool.http.HttpConnection.connect(HttpConnection.java:377)
at cn.hutool.http.HttpRequest.send(HttpRequest.java:1159)
... 3 more
14:18:57:25
Exception in thread "Thread-15" java.lang.RuntimeException: cn.hutool.core.io.IORuntimeException: ConnectException: Connection refused: connect
at boot.example.web.tomcat.ThreadTest.run(ThreadTest.java:24)
Caused by: cn.hutool.core.io.IORuntimeException: ConnectException: Connection refused: connect
at cn.hutool.http.HttpRequest.send(HttpRequest.java:1164)
at cn.hutool.http.HttpRequest.execute(HttpRequest.java:969)
at cn.hutool.http.HttpRequest.execute(HttpRequest.java:940)
at boot.example.web.tomcat.ThreadTest.run(ThreadTest.java:21)
Caused by: java.netCaused by: java.net.ConnectException: Connection refused: connect java.netat java.net.DualStackPlainSocketImpl.waitForConnect(Native Method) java.netat java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85) java.netat java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350) java.netat java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206) java.netat java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188) java.netat java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172) java.netat java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) java.netat java.net.Socket.connect(Socket.java:589) sun.netat sun.net.NetworkClient.doConnect(NetworkClient.java:175) sun.netat sun.net.www.http.HttpClient.openServer(HttpClient.java:463) sun.netat sun.net.www.http.HttpClient.openServer(HttpClient.java:558) sun.netat sun.net.www.http.HttpClient.<init>(HttpClient.java:242) sun.netat sun.net.www.http.HttpClient.New(HttpClient.java:339) sun.netat sun.net.www.http.HttpClient.New(HttpClient.java:357) sun.netat sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1220) sun.netat sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1156) sun.netat sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1050) sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:984)
at cn.hutool.http.HttpConnection.connect(HttpConnection.java:377)
at cn.hutool.http.HttpRequest.send(HttpRequest.java:1159)
... 3 more

关键点在于

Caused by: cn.hutool.core.io.IORuntimeException: ConnectException: Connection refused: connect

也就是说30次请求,总共处理了14次请求,剩下的16次请求被tomcat给拒绝了----达到tomcat的最大能接纳的请求数后拒绝多余的请求连接。

30次请求进入tomcat后,16条被拒接,14条请求被处理,处理先是最大线程数max-threads=6,在处理的时候,有6条请求加上正在处理的6条满足最大连接数max-connections=12,在这里,还有2条,是accept-count=2配置的2条,这2条请求任务恰恰是最容易忽略的,如此最终处理了14条请求,也就是说tomcat拒绝请求的条件是大于了最大连接数+队列长度的数量。

这里的测试是在设置很大超时时间下进行的

timeout(10000*1000)

tomcat参数调优配置(和硬件本身的支撑有很大关系)

server.tomcat.min-spare-threads=10

默认最小的线程数10就可以,这个参数没有多大必要去配置,默认的已经很好了,建议配置范围10-50之间

server.tomcat.max-threads = 200

处理任务的最大线程数默认200,一般应用都是支持的,如果要优化看应用(写的程序)复不复杂,需不需要依托计算机的算力,也就是会不会大量消耗cpu,如果大量消耗cpu,那么这个max-threads不能设置过大,如果仅仅只是普通的入库查询操作,增删改查,max-threads可以设置大一些,但是也不能过大,过大会导致请求的响应变慢 ,建议设置在200-1200之间,大概是min-space-threads的20倍

server.tomcat.max-connections = 10000

最大连接线程数,这个值默认10000已经够大了,有时候真正的业务还没有达到这个值都已经要多服务部署了,因此该参数没有增大的必要,但是可以改小,改到max-thread的20倍左右

server.tomcat.accept-count = 100

至于队列中的默认100这个值,也满足需求了,非要改建议大于min-spare-threads小于max-threads之间的某个倍数值就可以,这个参数不能设置太小。

一般的请求都是有超时机制的,一个http请求,可能几十秒后都还没有得到数据,那就会自动超时,自动超时并不代表被tomcat拒绝,可能是tomcat还没有开始处理到它

这里记录一下SpringBoot内置Tomcat配置多端口启动

    @Bean
    public TomcatServletWebServerFactory getFactory() {
        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
        Connector[] connectors = this.connectors();
        if (connectors != null && connectors.length > 0) {
            tomcat.addAdditionalTomcatConnectors(connectors);
        return tomcat;
    private Connector[] connectors() {
        String ports = "8081,8082,8083,8084";
        if (StringUtils.isEmpty(ports)) {
            return null;
        String[] port = ports.split(",");
        List<Connector> connectors = new ArrayList<>();
        for (String s : port) {
            Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
            connector.setPort(Integer.parseInt(s));
            connector.setScheme("http");
            //connector.setRedirectPort(8041);
            connectors.add(connector);
        return connectors.toArray(new Connector[]{});
   15:16:08.692 spring-boot-logging [restartedMain] INFO o.s.b.d.a.OptionalLiveReloadServer - LiveReload server is running on port 35729 
  
15:16:08.734 spring-boot-logging [restartedMain] INFO o.s.j.e.a.AnnotationMBeanExporter - Registering beans for JMX exposure on startup
15:16:08.752 spring-boot-logging [restartedMain] INFO o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8080"]
15:16:08.754 spring-boot-logging [restartedMain] INFO o.a.tomcat.util.net.NioSelectorPool - Using a shared selector for servlet write/read
15:16:08.767 spring-boot-logging [restartedMain] INFO o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8081"]
15:16:08.773 spring-boot-logging [restartedMain] INFO o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8082"]
15:16:08.777 spring-boot-logging [restartedMain] INFO o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8083"]
15:16:08.782 spring-boot-logging [restartedMain] INFO o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8084"]
15:16:08.797 spring-boot-logging [restartedMain] INFO o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port(s): 8080 (http) 8081 (http) 8082 (http) 8083 (http) 8084 (http) with context path ''
15:16:08.802 spring-boot-logging [restartedMain] INFO boot.example.web.tomcat.AppTomcat - Started AppTomcat in 5.725 seconds (JVM running for 6.629)
Hello World! Tomcat started on port(s): 8080 (http) 8081 (http) 8082 (http) 8083 (http) 8084 (http) with context path '' SpringBoot开发最大的好处是简化配置,内置了Tomcat, 在SpringBoot2.0.x版本中内置Tomcat版本是8.5.x,SpringBoot内置Tomcat的默认设置中,Tomcat的等待队列长度默认是100,Tomcat的最小工作线程数默认分配10,Tomcat的最大线程数是200,最大连接数是10000,至于最大并发量和最大连接数,常常理解成最大并发量就是最大连接数,实际上是有些牵强的,最大连接数并不一定就是最大并发量。
首先,线程是一个重点,每一次HTTP请求到达Web服务器,Web服务器都会创建一个线程处理该请求,该参数决定了应用服务同时可以处理多少个HTTP请求。 比较重要的有两个:初始线程最大线程。 初始线程:保障启动的时候,如果有大用户访问,能够很稳定的接受请求。最大线程:用来保证系统的稳定性。 超时时间:用来保障连接数不容易被压垮。如果大批的请求过来,延迟比较高,很容易把线程用光,这时就需要提高超时时间。这种情况在生产中是比较常见的 ,一旦网络不稳定,宁愿丢包也不能把服务器压垮。
日常应用中,单台Tomcat能支持最大并发是多少? 作为一个有经验的Java Web开发人员对这个问题应该有大概的印象,并会让问题再具体点,比如Tomcat版本,运行模式,并发请求允许的最大响应时间等,然后针对其中某个点搜索答案,而不应该低效的去直接搜这个答案。并且如果你没相关知识,很容易被网上的知识误导,因为很多都是很早之前配置的答案的转载。 以现在主要用的Tomcat8为例,...
springboot-configuration-metadata.json文件下面,有很多属于springboot得喷子hi属性,以下为tomcat的默认配置属性: server.tomcat.accept-count:等待队列长度,默认100(队列也做缓冲池用,但也不能无限长, 不但消耗内存,而且出队入队也消耗CPU) server.tomcat.max-connections:最大可被连接数,默认10000 server.tomcat.max-threads:最大工作线程,默认200,线程不是