Spring Boot内嵌Tomcat处理请求的链接数和线程数

news/2024/9/23 19:10:38/

Spring Boot内嵌Tomcat处理请求的连接数和线程数

处理请求的连接数和线程数配置

Spring Boot的配置项

#等待连接数
server.tomcat.accept-count=100
#最大链连接数
server.tomcat.max-connections=8192
#最小备用线程数
server.tomcat.threads.min-spare=10
#最大工作线程数
server.tomcat.threads.max=200

TomCat中的NIO模式的工作流程,主要分为Acceptor、Poller、Processor

Acceptor监听网络连接,有连接进来后注册在Poller中,Poller通过轮询检测连接中的读写事件,有事件发生时调用Processor进行请求处理。如下图:

Spring Boot内嵌Tomcat处理请求的链接数和线程数

源码说明(Spring Boot-2.7.18内嵌Tomcat-9.0.83)

线程数

线程数对应的是NIO模式图中的Executor,默认最大线程是200,核心线程数10。

  • server.tomcat.threads.min-spare 对应ThreadPoolExecutor的核心线程数
  • server.tomcat.threads.max 对应ThreadPoolExecutor的最大线程数
tomcatutilnetAbstractEndpoint_33">org.apache.tomcat.util.net.AbstractEndpoint
public void createExecutor() {internalExecutor = true;if (getUseVirtualThreads()) {executor = new VirtualThreadExecutor(getName() + "-virt-");} else {TaskQueue taskqueue = new TaskQueue();TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);taskqueue.setParent( (ThreadPoolExecutor) executor);}
}

ThreadPoolExecutor就是Tomcat的NIO模式下创建的线程池,其中getMinSpareThreads()getMaxThreads()分别获取的是核心线程数和最大线程数,对应的就是server.tomcat.threads.min-spareserver.tomcat.threads.max

tomcatutilthreadsTaskQueue_51">org.apache.tomcat.util.threads.TaskQueue

创建线程池的时候创建了一个TaskQueue,任务队列的处理逻辑主要在这里,下面分析一下TaskQueue的入队方法:

public boolean offer(Runnable o) {//1if (parent==null) {return super.offer(o);}//2if (parent.getPoolSizeNoLock() == parent.getMaximumPoolSize()) {return super.offer(o);}//3if (parent.getSubmittedCount() <= parent.getPoolSizeNoLock()) {return super.offer(o);}//4if (parent.getPoolSizeNoLock() < parent.getMaximumPoolSize()) {return false;}//5return super.offer(o);
}
  1. 如果parent(由 taskqueue.setParent( (ThreadPoolExecutor) executor)设置)属性为null,直接将任务添加到队列中等待执行
  2. 如果当前线程池中的活动线程数等于最大线程数,直接将任务添加到队列中等待执行
  3. 如果已提交但未开始执行的任务数小于或等于当前线程数,说明有足够的空闲线程来处理新任务,直接将任务添加到队列中等待执行
  4. 如果当前线程数大于最小线程数且小于最大线程数,需要创建新的线程来处理任务。返回false表示入队失败,ThreadPoolExecutor在检测到这种情况时会尝试创建一个新的线程来执行任务
  5. 线程池中有足够的资源或者队列尚未满时,直接将任务添加到队列中等待执行
tomcatutilthreadsThreadPoolExecutor_84">org.apache.tomcat.util.threads.ThreadPoolExecutor

下面分析一下返回false的情况

public void execute(Runnable command, long timeout, TimeUnit unit) {submittedCount.incrementAndGet();try {//这个方法executeInternal(command);} catch (RejectedExecutionException rx) {if (getQueue() instanceof TaskQueue) {final TaskQueue queue = (TaskQueue) 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;}}
}
 private void executeInternal(Runnable command) {if (command == null) {throw new NullPointerException();}int c = ctl.get();//1if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true)) {return;}c = ctl.get();}//2if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();if (! isRunning(recheck) && remove(command)) {reject(command);} else if (workerCountOf(recheck) == 0) {addWorker(null, false);}}else if (!addWorker(command, false)) {reject(command);}}
  1. 运行中的线程少于核心线程池大小(corePoolSize),尝试启动一个新线程,并将给定的任务作为其首个任务,如果成功则方法返回。
  2. 运行中的线程大于核心线程池大小(corePoolSize),检测线程池状态并执行入队操作。注意这里的workQueue就是在AbstractEndpoint.createExecutor()中构建ThreadPoolExecutor时注入的,如果 workQueue.offer(command) 入队成功,再次检查线程池的状态,确认是否需要撤销任务入队操作(如果线程池已经停止),或者启动一个新的非核心线程(如果当前没有工作线程)。
  3. 如果任务无法入队,即workQueue.offer(command)返回false的时候,则尝试添加一个新的非核心线程。如果失败,我们知道线程池要么已关闭,要么已饱和,因此拒绝该任务。
连接数

最大连接数对应的是NIO模式图中的Poller,用户请求进来后会注册到Poller中去,Poller可以处理的最大连接数就是server.tomcat.max-connections等待连接数对应的是NIO模式图中的Acceptor,在Acceptor中可以等待被accept方法调用返回的最大连接数就是server.tomcat.accept-count。如果accept方法执行的比较慢,短时间内大量请求的建立的TCP连接会被放在acceptCount定义大小的等待队列中,如果Poller可以处理的请求已达到最大值,并且不能及时处理完成,等待队列满了之后就会拒绝新的请求,并且没被及时处理的等待队列的TCP连接也会发出超时响应(connect timeout)。下面的方法一层层点击下去便会发现acceptCount影响的是C++层的TCP连接的队列大小。

    protected void initServerSocket() throws Exception {if (getUseInheritedChannel()) {//...省略} else if (getUnixDomainSocketPath() != null) {SocketAddress sa = JreCompat.getInstance().getUnixDomainSocketAddress(getUnixDomainSocketPath());serverSock = JreCompat.getInstance().openUnixDomainServerSocketChannel();serverSock.bind(sa, getAcceptCount());//这里获取//...省略} else {serverSock = ServerSocketChannel.open();socketProperties.setProperties(serverSock.socket());InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());serverSock.bind(addr, getAcceptCount());//这里获取}serverSock.configureBlocking(true);}
一个类比图

Spring Boot内嵌Tomcat处理请求的链接数和线程数


http://www.ppmy.cn/news/1508921.html

相关文章

lvs的相关应用2

lvs 安装lvs 配置规则&#xff0c;查看所有的规则&#xff0c;如果已经配置好规则&#xff0c;重启之后就没了 [rootds01 ~]# ipvsadm -Ln IP Virtual Server version 1.2.1 (size4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forwa…

LVS部分配置1

LVS nat服务器&#xff08;作时间服务器&#xff09; [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 [rootlocalhost ~]# yum -y install ntpdate [rootlocalhost ~]# ntpdate cn.ntp.org.cn [rootlocalhost ~]# which ntpdate [rootlocalhost…

平安城市行业无人机解决方案(网格化巡逻场景应用)

场景痛点&#xff1a; 缺少飞手&#xff0c;导致无人机应用频次偏低 无人机需要人工换电池、维护及存储 日常巡逻 | 无人机补盲巡逻 大疆航点飞行功能&#xff0c;重复性任务只飞一遍&#xff0c;后续重复调用 无人机航点规划&#xff0c;一次设置&#xff0c;后续重复调用…

Cmake编译工程

Cmake目录结构&#xff1a;项目主目录中会放一个CmakeList.txt的文本文档&#xff0c;后期使用cmake指令时候&#xff0c;依赖的就是该文档 1.包含源文件的子文件夹包含Cmakelist.txt文件时&#xff0c;主目录的Cmakelists.txtx要通过add_subdirector添加子目录 2.包含源文件…

8月13日学习笔记 LVS

一.描述以及工作原理 1. 什么是LVS linux virtural server的简称&#xff0c;也就是linxu虚拟机服务器&#xff0c;这是一个 由章文嵩博士发起的开源项目&#xff0c;官网是 http://www.linuxvirtualserver.org,现在lvs已经是linux内核标 准的一部分&#xff0c;使用lvs可以达…

如何获取VS Code扩展的版本更新信息

获取VS Code 扩展的版本更新的需求 因为企业内部有架设私有扩展管理器的要求&#xff0c;但是对于一些官方市场的插件&#xff0c;希望可以自动获取这些扩展的更新并上传至私有扩展管理器。于是就有了本篇介绍的需求&#xff1a; 通过API的方式获取VS Code 扩展的更新。 关于…

day02--HTML CSS

一、HTML表单 表单的作用是用于采集用户再页面上填入的数据&#xff0c;并发送给后端服务器&#xff0c;经常用于用户注册、登录、xx信息添加、xx信息修改 1.1表单 1、input表示文本框 type属性&#xff1a;负责配置不同的输入框类型 text&#xff1a;普通文本框 password&…

C++ 语言特性01 - 基本数据类型

目录 0. 概述: 1. C++ 中基本数据类型及位宽(Bits) 2. C++ 中基本数据类型的取值范围 ​3. 无符号和有符号整数表示 4. 定点数表示 5. 浮点数表示 6. C++ 11 新增的数据类型 long long 0. 概述: 本文介绍下C++中的基本数据类型,它是C++编程基础,应理解数据类型的位宽…