SpringBoot+内置Tomcat配置,参数调优,最大并发量,最大连接数

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;

内置的tomcat

找到ServerProperties中的public static class Tomcat对象

package org.springframework.boot.autoconfigure.web;

…………

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
        //内容
}

在public static class Tomcat中有很多配置


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

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

        /**
         * 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 a***epts and processes at any
         * given time. Once the limit has been reached, the operating system may still
         * a***ept connections based on the "a***eptCount" property.
         */
        private int maxConnections = 10000;

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

默认最大连接数maxConnections = 10000

默认队列长度a***eptCount = 100

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

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

也就是说配置如下

server.tomcat.a***ept-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 a***epts and processes at any given time. Once the limit has been reached, the operating system may still a***ept connections based on the "a***eptCount" property
服务器在任何给定时间接受和处理的最大连接数。一旦达到限制,操作系统仍然可以根据“a***eptCount”属性接受连接

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

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

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

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

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

通常对SpringBoot内置Tomcat调优主要是针对最大连接数(maxConnections = 10000),队列长度(a***eptCount = 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.a***ept-count = 2
server.tomcat.max-connections = 12
server.tomcat.max-threads = 6
server.tomcat.min-spare-threads=3

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

新建一个测试线程

package boot.example.web.tomcat;

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

关键点在于

Caused by: ***.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条,是a***ept-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.a***ept-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.***.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 ''

转载请说明出处内容投诉
CSS教程_站长资源网 » SpringBoot+内置Tomcat配置,参数调优,最大并发量,最大连接数

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买