返回顶部
首页 > 资讯 > 后端开发 > JAVA >图解系列 图解Spring Boot 最大连接数及最大并发数
  • 339
分享到

图解系列 图解Spring Boot 最大连接数及最大并发数

springbootjavatomcat 2023-09-10 17:09:32 339人浏览 安东尼
摘要

文章目录 概序架构图TCP的3次握手4次挥手时序图核心参数AcceptCountMaxConnectionsMinSpareThread/MaxThreadMaxKeepAliveReques

文章目录

每个Spring Boot版本和内置容器不同,结果也不同,这里以Spring Boot 2.7.10版本 + 内置Tomcat容器举例。

概序

SpringBoot2.7.10版本中内置Tomcat版本是9.0.73SpringBoot内置Tomcat的默认设置如下:

  • Tomcat的连接等待队列长度,默认是100
  • Tomcat的最大连接数,默认是8192
  • Tomcat的最小工作线程数,默认是10
  • Tomcat的最大线程数,默认是200
  • Tomcat的连接超时时间,默认是20s

相关配置及默认值如下

server:  tomcat:    # 当所有可能的请求处理线程都在使用中时,传入连接请求的最大队列长度    accept-count: 100    # 服务器在任何给定时间接受和处理的最大连接数。一旦达到限制,操作系统仍然可以接受基于“acceptCount”属性的连接。    max-connections: 8192    threads:      # 工作线程的最小数量,初始化时创建的线程数      min-spare: 10      # 工作线程的最大数量 io密集型建议10倍的cpu数,cpu密集型建议cpu数+1,绝大部分应用都是io密集型      max: 200    # 连接器在接受连接后等待显示请求 URI 行的时间。    connection-timeout: 20000    # 在关闭连接之前等待另一个 Http 请求的时间。如果未设置,则使用 connectionTimeout。设置为 -1 时不会超时。    keep-alive-timeout: 20000    # 在连接关闭之前可以进行流水线处理的最大HTTP请求数量。当设置为0或1时,禁用keep-alive和流水线处理。当设置为-1时,允许无限数量的流水线处理或keep-alive请求。     max-keep-alive-requests: 100

架构

当连接数大于maxConnections+acceptCount + 1时,新来的请求不会收到服务器拒绝连接响应,而是不会和新的请求进行3次握手建立连接,一段时间后(客户端的超时时间或者Tomcat的20s后)会出现请求连接超时。

tcp的3次握手4次挥手

时序图

核心参数

AcceptCount

全连接队列容量,等同于backlog参数,与linux中的系统参数somaxconn取较小值,windows中没有系统参数。

NioEndpoint.java

serverSock = ServerSocketChannel.open();socketProperties.setProperties(serverSock.socket());InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());// 这里serverSock.socket().bind(addr,getAcceptCount());

MaxConnections

Acccptor.java

// 线程的run方法。public void run() {    while (!stopCalled) {    // 如果我们已达到最大连接数,等待        connectionLimitLatch.countUpOrAwait();            // 接受来自服务器套接字的下一个传入连接            socket = endpoint.serverSocketAccept()            // socket.close 释放的时候 调用 connectionLimitLatch.countDown();                

MinSpareThread/MaxThread

AbstractEndpoint.java

// tomcat 启动时public void createExecutor() {        internalExecutor = true;    // 容量为Integer.MAX_VALUE        TaskQueue taskqueue = new TaskQueue();        TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());    // Tomcat扩展的线程池        executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);        taskqueue.setParent( (ThreadPoolExecutor) executor);}

重点重点重点

Tomcat扩展了线程池增强了功能。

  • JDK线程池流程:minThreads --> queue --> maxThreads --> Exception
  • Tomcat增强后: minThreads --> maxThreads --> queue --> Exception

MaxKeepAliveRequests

长连接,在发送了maxKeepAliveRequests个请求后就会被服务器端主动断开连接。

在连接关闭之前可以进行流水线处理的最大HTTP请求数量。当设置为0或1时,禁用keep-alive和流水线处理。当设置为-1时,允许无限数量的流水线处理或keep-alive请求。

较大的 MaxKeepAliveRequests 值可能会导致服务器上的连接资源被长时间占用。根据您的具体需求,您可以根据服务器的负载和资源配置来调整 MaxKeepAliveRequests 的值,以平衡并发连接和服务器资源的利用率。

NIOEndpoint.setSocketOptionssocketWrapper.seTKEepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());Http11Processor.service(SocketWrapperBase<?> socketWrapper)  keepAlive = true;  while(!getErrorState().isError() && keepAlive && !isAsync() && upgradeToken == null &&                sendfileState == SendfileState.DONE && !protocol.isPaused()) {    // 默认100  int maxKeepAliveRequests = protocol.getMaxKeepAliveRequests();if (maxKeepAliveRequests == 1) {    keepAlive = false;} else if (maxKeepAliveRequests > 0 &&            //            socketWrapper.decrementKeepAlive() <= 0) {    keepAlive = false;}

ConnectionTimeout

连接的生存周期,当已经建立的连接,在connectionTimeout时间内,如果没有请求到来,服务端程序将会主动关闭该连接。

  • 在Tomcat 9中,ConnectionTimeout的默认值是20000毫秒,也就是20秒。
  • 如果该时间过长,服务器将要等待很长时间才会收到客户端的请求结果,从而导致服务效率低下。如果该时间过短,则可能会出现客户端在请求过程中网络慢等问题,而被服务器取消连接的情况。
  • 由于某个交换机或者路由器出现了问题,导致某些post大文件的请求堆积在交换机或者路由器上,tomcat的工作线程一直拿不到完整的文件数据。

NioEndpoint.Poller#run()

 // Check for read timeout if ((socketWrapper.interestOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {     long delta = now - socketWrapper.getLastRead();     long timeout = socketWrapper.getReadTimeout();     if (timeout > 0 && delta > timeout) {         readTimeout = true;     } } // Check for write timeout if (!readTimeout && (socketWrapper.interestOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) {     long delta = now - socketWrapper.getLastWrite();     long timeout = socketWrapper.getWriteTimeout();     if (timeout > 0 && delta > timeout) {         writeTimeout = true;     } }

KeepAliveTimeout

等待另一个 HTTP 请求的时间,然后关闭连接。当未设置时,将使用 connectionTimeout。当设置为 -1 时,将没有超时。

Http11InputBuffer.parseRequestLine

// Read new bytes if neededif (byteBuffer.position() >= byteBuffer.limit()) {    if (keptAlive) {        // 还没有读取任何请求数据,所以使用保持活动超时        wrapper.setReadTimeout(keepAliveTimeout);    }    if (!fill(false)) {        // A read is pending, so no longer in initial state        parsingRequestLinePhase = 1;        return false;    }    // 至少已收到请求的一个字节 切换到套接字超时。     wrapper.setReadTimeout(connectionTimeout);}

内部线程

Acceptor

Acceptor: 接收器,作用是接受scoket网络请求,并调用setSocketOptions()封装成为NiOSocketWrapper,并注册到Poller的events中。注意查看run方法org.apache.tomcat.util.net.Acceptor#run

public void run() {       while (!stopCalled) {           // 等待下一个请求进来           socket = endpoint.serverSocketAccept();            // 注册socket到Poller,生成PollerEvent事件           endpoint.setSocketOptions(socket);           // 向轮询器注册新创建的套接字                    - poller.reGISter(socketWrapper);                        - (SynchronizedQueue(128))events.add(new PollerEvent(socketWrapper))    

Poller

Poller:轮询器,轮询是否有事件达到,有请求事件到达后,以NIO的处理方式,查询Selector取出所有请求,遍历每个请求的需求,分配给Executor线程池执行。查看org.apache.tomcat.util.net.NioEndpoint.Poller#run()

public void run() {       while (true) {               //查询selector取出所有请求事件               Iterator<SelectionKey> iterator =                   keyCount > 0 ? selector.selectedKeys().iterator() : null;               // 遍历就绪键的集合并调度任何活动事件。               while (iterator != null && iterator.hasNext()) {                   SelectionKey sk = iterator.next();                   iterator.remove();                   NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();                   // 分配给Executor线程池执行处理请求key                   if (socketWrapper != null) {                       processKey(sk, socketWrapper);                       - processSocket(socketWrapper, SocketEvent.OPEN_READ/SocketEvent.OPEN_WRITE)                           - executor.execute((Runnable)new SocketProcessor(socketWrapper,SocketEvent))                   }               }

TomcatThreadPoolExecutor

真正执行连接读写操作的线程池,在jdk线程池的基础上进行了扩展优化

AbstractEndpoint.java

public void createExecutor() {        internalExecutor = true;        TaskQueue taskqueue = new TaskQueue();        TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());    // tomcat自定义线程池        executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);        taskqueue.setParent( (ThreadPoolExecutor) executor);    }

TomcatThreadPoolExecutor.java

// 与 java.util.concurrent.ThreadPoolExecutor 相同,但实现了更高效的getSubmittedCount()方法,用于正确处理工作队列。// 如果未指定 RejectedExecutionHandler,将配置一个默认的,并且该处理程序将始终抛出 RejectedExecutionExceptionpublic class ThreadPoolExecutor extends java.util.concurrent.ThreadPoolExecutor {// 已提交但尚未完成的任务数。这包括队列中的任务和已交给工作线程但后者尚未开始执行任务的任务。    // 这个数字总是大于或等于getActiveCount() 。    private final AtomicInteger submittedCount = new AtomicInteger(0);        @Override    protected void afterExecute(Runnable r, Throwable t) {        if (!(t instanceof StopPooledThreadException)) {            submittedCount.decrementAndGet();        }    @Override    public void execute(Runnable command){        // 提交任务的数量+1        submittedCount.incrementAndGet();        try {            //  线程池内部方法,真正执行的方法。就是JDK线程池原生的方法。            super.execute(command);        } catch (RejectedExecutionException rx) {            // 再次把被拒绝的任务放入到队列中。            if (super.getQueue() instanceof TaskQueue) {                final TaskQueue queue = (TaskQueue)super.getQueue();                try {                      //强制的将任务放入到阻塞队列中                    if (!queue.force(command, timeout, unit)) {                        //放入失败,则继续抛出异常                        submittedCount.decrementAndGet();                        throw new RejectedExecutionException(sm.getString("threadPoolExecutor.queueFull"));                    }                } catch (InterruptedException x) {                     //被中断也抛出异常                    submittedCount.decrementAndGet();                    throw new RejectedExecutionException(x);                }            } else {                 //不是这种队列,那么当任务满了之后,直接抛出去。                submittedCount.decrementAndGet();                throw rx;            }        }    }    
public class TaskQueue extends LinkedBlockingQueue<Runnable> {    private static final long serialVersionUID = 1L;    private transient volatile ThreadPoolExecutor parent = null;    private static final int DEFAULT_FORCED_REMAINING_CAPACITY = -1;        private int forcedRemaininGCapacity = -1;        public TaskQueue() {    }    public TaskQueue(int capacity) {        super(capacity);    }    public TaskQueue(Collection<? extends Runnable> c) {        super(c);    }        public void setParent(ThreadPoolExecutor parent) {        this.parent = parent;    }        public boolean force(Runnable o) {        if (parent == null || parent.isshutdown()) {            throw new RejectedExecutionException("taskQueue.notRunning");        }        return super.offer(o);    }        @Deprecated    public boolean force(Runnable o, long timeout, TimeUnit unit) throws InterruptedException {        if (parent == null || parent.isShutdown()) {            throw new RejectedExecutionException("taskQueue.notRunning");        }        return super.offer(o, timeout, unit); //forces the item onto the queue, to be used if the task is rejected    }        @Override    public boolean offer(Runnable runnable) {        if (parent == null) {            return super.offer(runnable);        }        //若是达到最大线程数,进队列。        if (parent.getPoolSize() == parent.getMaximumPoolSize()) {            return super.offer(runnable);        }        //当前活跃线程为10个,但是只有8个任务在执行,于是,直接进队列。        if (parent.getSubmittedCount() < (parent.getPoolSize())) {            return super.offer(runnable);        }        //当前线程数小于最大线程数,那么直接返回false,去创建最大线程        if (parent.getPoolSize() < parent.getMaximumPoolSize()) {            return false;        }        //否则的话,将任务放入到队列中        return super.offer(runnable);    }        @Override    public Runnable poll(long timeout, TimeUnit unit) throws InterruptedException {        Runnable runnable = super.poll(timeout, unit);        //取任务超时,会停止当前线程,来避免内存泄露        if (runnable == null && parent != null) {            parent.stopCurrentThreadIfNeeded();        }        return runnable;    }        @Override    public Runnable take() throws InterruptedException {        //当前线程应当被终止的情况下:        if (parent != null && parent.currentThreadShouldBeStopped()) {            long keepAliveTime = parent.getKeepAliveTime(TimeUnit.MILLISECONDS);            return poll(keepAliveTime, TimeUnit.MILLISECONDS);        }        return super.take();    }        @Override    public int remainingCapacity() {        if (forcedRemainingCapacity > DEFAULT_FORCED_REMAINING_CAPACITY) {            return forcedRemainingCapacity;        }        return super.remainingCapacity();    }        public void setForcedRemainingCapacity(int forcedRemainingCapacity) {        this.forcedRemainingCapacity = forcedRemainingCapacity;    }        void resetForcedRemainingCapacity() {        this.forcedRemainingCapacity = DEFAULT_FORCED_REMAINING_CAPACITY;    }}

JDK线程池架构图

在这里插入图片描述

Tomcat线程架构

测试

如下配置举例

server:  port: 8080  tomcat:    accept-count: 3    max-connections: 6    threads:      min-spare: 2      max: 3

使用ss -nlt查看全连接队列容量。

ss -nltpss -nlt|grep 8080- Recv-Q表示(acceptCount)全连接队列目前长度- Send-Q表示(acceptCount)全连接队列的容量。

静默状态

6个并发连接

结果同上

9个并发连接

10个并发连接

11个并发连接

结果同上

使用ss -nt查看连接状态。

ss -ntpss -nt|grep 8080- Recv-Q表示客户端有多少个字节发送但还没有被服务端接收- Send-Q就表示为有多少个字节未被客户端接收。

静默状态

6个并发连接

9个并发连接

补充个netstat

10个并发连接

结果同上,队列中多加了个

11个并发连接

超出连接后,会有个连接一直停留在SYN_RECV状态,不会完成3次握手了。

超出连接后客户端一直就停留在SYN-SENT状态,服务端不会再发送SYN+ACK,直到客户端超时(20s内核控制)断开。

客户端请求超时(需要等待一定时间(20s))。

这里如果客户端设置了超时时间,要和服务端3次握手超时时间对比小的为准。

12个并发连接

参考

  • https://www.zhangbj.com/p/1105.html
  • https://www.eginnovations.com/blog/tomcat-monitoring-metrics/

来源地址:https://blog.csdn.net/abu935009066/article/details/130957301

--结束END--

本文标题: 图解系列 图解Spring Boot 最大连接数及最大并发数

本文链接: https://lsjlt.com/news/402477.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 图解系列 图解Spring Boot 最大连接数及最大并发数
    文章目录 概序架构图TCP的3次握手4次挥手时序图核心参数AcceptCountMaxConnectionsMinSpareThread/MaxThreadMaxKeepAliveReques...
    99+
    2023-09-10
    spring boot java tomcat
  • 如何理解IIS最大并发连接数
    本篇文章为大家展示了如何理解IIS最大并发连接数,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。最大并发连接数=队列长度+工作线程数【工作线程数】IIS实际可以第一时间处理的请求数。比如,工作线程数=...
    99+
    2023-06-05
  • 查看mysql的最大连接数,并修改最大连接数
    查看当前最大连接数 show variables like 'max_connections'; mysql默认最大连接数为151,最大连接数为100000 通过命令修改最大连接数 set global max_connections...
    99+
    2023-08-30
    mysql 数据库 linux
  • springboot tomcat最大线程数与最大连接数解析
    springboot tomcat最大线程数与最大连接数 首先看看springboot内置的tomcat,该如何配置这两个参数 # 在配置文件中添加如下内容 # tomcat最大...
    99+
    2024-04-02
  • Mysql查看最大连接数和修改最大连接数的讲解
    MySQL查看最大连接数和修改最大连接数 1、查看最大连接数 show variables like '%max_connections%'; ...
    99+
    2024-04-02
  • mysql中最大连接数、最大并发线程数的区别有哪些
    这篇文章将为大家详细讲解有关mysql中最大连接数、最大并发线程数的区别有哪些,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。show variables like ...
    99+
    2024-04-02
  • 超出MySQL最大连接数问题及解决
    目录超出mysql最大连接数问题查看最大连接数修改最大连接数MySQL最大连接数,TimeOut配置MySQL连接数配置Mysql的连接线程池Mysql TimeOut配置总结超出MySQL最大连接数问题 如果遇到MyS...
    99+
    2023-05-15
    MySQL最大连接数 MySQL连接数 超出MySQL最大连接数
  • Nginx最大连接数配置详解
    本文实例为大家分享了Nginx最大连接数配置的具体代码,供大家参考,具体内容如下 配置Nginx最大工作进程数## worker_processes:最大工作进程数 work_co...
    99+
    2024-04-02
  • mysql 最大连接数max_connections解决办法
    问题原因 1)客户端程序在退出之前没有调用mysql_close()。[写程序的疏忽,或者数据库的db类库没有自动关闭每次的连接] 2)客户端sleep的时间在wait_timeout或interactive_timeout规定的秒内没有发...
    99+
    2023-09-21
    mysql 数据库 服务器
  • 详解如何修改MySQL最大连接数
    修改 mysql 最大连接数 JDBC 操作MySQL数据库的时候 遇到这行报错 很明显 是数据库连接数超限了修改MySQL数据库连接数 具体操作如下 查看 MySQL 数据库当前默认支持最大连接数 show varia...
    99+
    2022-08-09
    修改MySQL最大连接数 MySQL最大连接数
  • mysql怎么设置最大连接数详解
    目录前言方法一:命令行修改方法二:通过mysql配置文件来修改最大连接数总结前言 设置mysql最大连接数的方法:首先打开mysql的控制台;然后输入语句【set GLOBAL ma...
    99+
    2024-04-02
  • mysql如何设置最大连接数详解
    这篇文章将为大家详细讲解有关mysql如何设置最大连接数详解,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。设置mysql最大连接数的方法:方法一:命令行修改我们只需要打开mysql的控制台,输入“set ...
    99+
    2023-06-29
  • Linux 系统上,修改最大连接数
    在 Linux 系统上,修改最大连接数需要修改内核参数,具体操作步骤如下: 打开 /etc/sysctl.conf 文件: sudo vi /etc/sysctl.conf 在文件末尾添加如下内容: 增加系统最大文件句柄数 fs.file-...
    99+
    2023-08-21
    linux 服务器 运维
  • Spring Boot中内置Tomcat最大连接数、线程数与等待数 实践调优
    在 Spring Boot 框架中,我们使用最多的是Tomcat,这是 Spring Boot 默认的容器技术,而且是内嵌式的 Tomcat。Tomcat 是 Apache 基金下的一个轻量级的Ser...
    99+
    2023-08-31
    tomcat spring boot java
  • php给定一个数组并求连接最大值
    PHP是一种被广泛用于web开发的脚本语言,拥有着强大的数组操作能力。本文将介绍如何用PHP来给定一个数组并求得它的连接最大值。什么是连接最大值?在计算机科学中,连接是将两个或多个字符串合并成一个更长的字符串的操作。例如,将字符串“hell...
    99+
    2023-05-23
  • MongoDB中最大连接数设置失效如何解决
    MongoDB中最大连接数设置失效如何解决,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。案例重现以下为本次测试MongoDB案例配置的参数。启...
    99+
    2024-04-02
  • 超出MySQL最大连接数的问题怎么解决
    今天小编给大家分享的是超出MySQL最大连接数的问题怎么解决,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起往下看吧。一定会有所收获的哦。超出MySQL最大连接数问题如果遇到MySQ连接数超出最大限制了,不要慌,往...
    99+
    2023-07-06
  • 如何测试Linux下tcp最大连接数限制详解
    前言 关于TCP服务器最大并发连接数有一种误解就是“因为端口号上限为65535,所以TCP服务器理论上的可承载的最大并发连接数也是65535”。 先说结论:对于TCP服务端进程来说,他可以同时连接的客户端数量并不受限于可...
    99+
    2022-06-04
    linux tcp连接数设置 linux查看tcp连接数 linux tcp连接数限制
  • Java并发编程中如何最大化利用接口和数组?
    Java是一种面向对象的编程语言,面向对象的核心思想是“封装、继承、多态”。在Java中,接口和数组是非常重要的概念。接口提供了一种规范,可以让我们定义一组方法,而不需要实现它们。数组是一种非常方便的数据结构,可以存储一组相同类型的数据。...
    99+
    2023-10-18
    接口 并发 数组
  • 大数据开发phoenix连接hbase流程详解
    目录一、安装phoennix添加配置二、启动phoenix服务三、phoenix常用语法四、Java代码集成phoenix一、安装phoennix添加配置 1、将phoenix-server-hbase-2.4-5.1...
    99+
    2022-11-20
    phoenix连接hbase 大数据phoenix
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作