SpringBoot 内置 Tomcat 线程数优化配置,你学会了吗?
前言
本文解析springboot内置tomcat调优并发线程数的一些参数,并结合源码进行分析
参数
线程池核心线程数
server.tomcat.min-spare-threads:该参数为tomcat处理业务的核心线程数大小,默认值为10
线程池最大线程数
server.tomcat.max-threads:该参数为tomcat处理业务的最大线程数大小,默认值为200,当对并发量有一点值时可以调大该参数
请求最大连接数
server.tomcat.max-connections:该参数为请求的最大连接数,默认值为10000,注意这个参数并不是设置在线程池上的,而是在tomcat的Acceptor类(专门处理连接的线程类)来控制的,结合源码我们可以看到
protected void countUpOrAwaitConnection() throws InterruptedException {if (maxConnections==-1) return;LimitLatch latch = connectionLimitLatch;if (latch!=null) latch.countUpOrAwait();}
可以看到当最大连接数满了之后会进行等待
accept-count
server.tomcat.accept-count:这个参数实际上和tomcat没有太大关系,默认值为100
我们先看下文档的定义
/*** Allows the server developer to specify the acceptCount (backlog) that* should be used for server sockets. By default, this value* is 100.*/private int acceptCount = 100;
这个参数是服务端创建ServerSocket时操作系统控制同时连接的最大数量的,服务端接收连接是通过accept()来的,accept()是非常快的,所以accept-count的不需要太大,正常保持默认值100即可了,acceptCount这个参数和线程池无关,会被映射为backlog参数,是socket的参数,在源码的使用是在NioEndpoint类的initServerSocket方法,在tomcat中的名字是backlog在springboot内置tomcat中名字没有使用backlog而是使用acceptCount
serverSock.socket().bind(addr,getAcceptCount())
protected void initServerSocket() throws Exception {if (!getUseInheritedChannel()) {serverSock = ServerSocketChannel.open();socketProperties.setProperties(serverSock.socket());InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));// 核心代码serverSock.socket().bind(addr,getAcceptCount());} else {// Retrieve the channel provided by the OSChannel ic = System.inheritedChannel();if (ic instanceof ServerSocketChannel) {serverSock = (ServerSocketChannel) ic;}if (serverSock == null) {throw new IllegalArgumentException(sm.getString("endpoint.init.bind.inherited"));}}serverSock.configureBlocking(true); //mimic APR behavior}
tomcat线程池处理机制
tomcat最终使用线程池来处理业务逻辑,java默认的线程池的规则:
核心线程数满了则优先放入队列,当队列满了之后才会创建非核心线程来处理,那么tomcat是这样做的吗?
首先如果tomcat这样做,那么当达到核心线程后后续任务就需要等待了,这显然是不合理的,我们通过源码来看下tomcat是如何处理的
在AbstractEndpoint的createExecutor创建了处理业务数据的线程池
public void createExecutor() {internalExecutor = true;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);}
主要是使用了TaskQueue队列,ThreadPoolExecutor并不是jdk的,而是tomcat重写的。
我们从线程池的处理方法execute看起
public void execute(Runnable command) {execute(command,0,TimeUnit.MILLISECONDS);}
public void execute(Runnable command, long timeout, TimeUnit unit) {submittedCount.incrementAndGet();try {// 核心代码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("Queue capacity is full.");}} catch (InterruptedException x) {submittedCount.decrementAndGet();throw new RejectedExecutionException(x);}} else {submittedCount.decrementAndGet();throw rx;}}}
又调用会jdk的execute了
public void execute(Runnable command) {if (command == null)throw new NullPointerException();int c = ctl.get();// 1、工作线程数小于核心线程数则添加任务,核心线程会处理if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true))return;c = ctl.get();}// 2、工作线程不小于核心线程数,则放到workQueue队列中if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();if (! isRunning(recheck) && remove(command))reject(command);else if (workerCountOf(recheck) == 0)addWorker(null, false);}// 3、否则添加任务,addWorker会进行创建线程else if (!addWorker(command, false))reject(command);}
从这里可以看到jdk线程池的机制,tomcat使用了自己的TaskQueue队列,所以我们看代码2处当核心线程用完了会调用队列的offer方法
我们看TaskQueue的offer
public boolean offer(Runnable o) {//we can't do any checks// parent就是指线程池,没有线程池则添加到队列if (parent==null) return super.offer(o);//we are maxed out on threads, simply queue the object// 线程数量已经达到了最大线程数,那么只能添加到队列if (parent.getPoolSize() == parent.getMaximumPoolSize()) return super.offer(o);//we have idle threads, just add it to the queue// 如果当前处理的任务数量小于当前线程池中线程的数量,那么任务放到线程池,即相当于马上会有空闲线程来处理if (parent.getSubmittedCount()<=(parent.getPoolSize())) return super.offer(o);//if we have less threads than maximum force creation of a new thread// TODO 核心代码,如果当前线程数量还没有达到线程池最大线程池的数量,那么就直接创建线程,这里返回falseif (parent.getPoolSize()<parent.getMaximumPoolSize()) return false;//if we reached here, we need to add it to the queue// 最后的策略,放到队列return super.offer(o);}
可以看到当执行offer时,不是直接放到队列的,当线程池总线程数量还没达到线程池最大线程数时会返回false,返回false时就会执行线程池execute的代码3处,执行addWorker(command, false),也就开始创建新的线程来处理当前任务了
总结
tomcat主要通过使用自己的TaskQueue队列来对线程池做出了不同的策略,也就是tomcat当线程数大于核心数时就会直接创建新的线程来处理,而不是放到队列
SpringBoot 内置 Tomcat 线程数优化配置,你学会了吗?相关推荐
- windows查看tomcat连接数_Springboot内置Tomcat线程数优化
# 等待队列长度,默认100.队列也做缓冲池用,但也不能无限长,不但消耗内存,而且出队入队也消耗CPU server.tomcat.accept-count=1000 # 最大工作线程数,默认200. ...
- 优化之SpringBoot 内置tomcat 调优测试
问题 怎么配置springBoot 内置tomcat,才能使得自己的服务效率更高呢? 基础配置 Spring Boot 能支持的最大并发量主要看其对Tomcat的设置,可以在配置文件中对其进行更改.我 ...
- Springboot内置Tomcat配置参数调优
Springboot内置Tomcat配置参数调优,首先,线程数是一个重点,每一次HTTP请求到达Web服务器,Web服务器都会创建一个线程来处理该请求,该参数决定了应用服务同时可以处理多少个HTTP请 ...
- SpringBoot内置Tomcat支持多大并发量和连接数
SpringBoot内置Tomcat,再默认设置中,Tomcat的最大线程数是200,最大连接数是10000.支持的并发量是指连接数,200个线程如何处理10000条连接的? Tomcat有两种处理连 ...
- Spring Boot 内置Tomcat——IntelliJ IDEA中配置模块目录设为文档根目录(DocumentRoot)解决方案
源码分析 org.springframework.boot.web.servlet.server.DocumentRoot /*** Returns the absolute document roo ...
- SpringBoot内置tomcat出现error:An incompatible version [1.1.32] of the APR based Apache Tomcat Native lib
SpringBoot内置tomcat出现error:An incompatible version [1.1.32] of the APR based Apache Tomcat Native lib ...
- SpringBoot内置tomcat出现APR版本过低解决办法
SpringBoot内置tomcat出现error:An incompatible version [1.1.32] of the APR based Apache Tomcat Native lib ...
- SpringBoot内置Tomcat启动不了的原因
SpringBoot内置Tomcat启动不了的原因: 1.需要加入spring-boot-starter-web依赖 [web中集成了tomcat.dispatcherServlet.xml-] &l ...
- SpringBoot内置Tomcat浅析
一.SpringBoot框架内置Tomcat,开发非常方便,随着SpringBoot的框架升级,内置Tomcat也更新版本.本文SpringBoot框架版本:2.2.10. 1.如何查看SpringB ...
最新文章
- ORA-01747: user.table.column, table.column 或列说明无效 异常解决方法总结
- TCP/IP详解--学习笔记(5)-IP选路,动态选路,和一些细节
- Sql自动更新不同IP的数据库数据。(link Server)
- go语言 不支持动态加载_动态语言支持
- linux qt 音频文件怎么打开,Qt:获取Linux中可用音频设备的列表
- vue_ form表单 v-model
- JavaEE Tutorials (13) - 使用锁定控制对实体数据的并发访问
- 95-242-040-源码-快照-Flink 分布式快照的设计-存储
- 解决数据库导入导出的常见问题集解决办法
- 关于telnet的问题
- [转贴]从零开始学C++之STL(二):实现一个简单容器模板类Vec(模仿VC6.0 中 vector 的实现、vector 的容量capacity 增长问题)...
- asp.net Json序列化
- python shelve模块_python中的Shelve模块不工作:“无法确定db type”
- 微博html5版登录网址,微博网页版登录入口
- mac php71 php fpm,Mac PHP-fpm
- xp外观主题下载_增强Windows XP外观的简便方法
- 淘东电商项目(68) -互联网安全架构设计(黑名单拦截及MD5加签)
- Ubuntu制作本地源
- 70张让你大开眼界的照片(配…
- 计算某年某月某日是该年中的第几天