前言

本文解析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 线程数优化配置,你学会了吗?相关推荐

  1. windows查看tomcat连接数_Springboot内置Tomcat线程数优化

    # 等待队列长度,默认100.队列也做缓冲池用,但也不能无限长,不但消耗内存,而且出队入队也消耗CPU server.tomcat.accept-count=1000 # 最大工作线程数,默认200. ...

  2. 优化之SpringBoot 内置tomcat 调优测试

    问题 怎么配置springBoot 内置tomcat,才能使得自己的服务效率更高呢? 基础配置 Spring Boot 能支持的最大并发量主要看其对Tomcat的设置,可以在配置文件中对其进行更改.我 ...

  3. Springboot内置Tomcat配置参数调优

    Springboot内置Tomcat配置参数调优,首先,线程数是一个重点,每一次HTTP请求到达Web服务器,Web服务器都会创建一个线程来处理该请求,该参数决定了应用服务同时可以处理多少个HTTP请 ...

  4. SpringBoot内置Tomcat支持多大并发量和连接数

    SpringBoot内置Tomcat,再默认设置中,Tomcat的最大线程数是200,最大连接数是10000.支持的并发量是指连接数,200个线程如何处理10000条连接的? Tomcat有两种处理连 ...

  5. Spring Boot 内置Tomcat——IntelliJ IDEA中配置模块目录设为文档根目录(DocumentRoot)解决方案

    源码分析 org.springframework.boot.web.servlet.server.DocumentRoot /*** Returns the absolute document roo ...

  6. 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 ...

  7. SpringBoot内置tomcat出现APR版本过低解决办法

    SpringBoot内置tomcat出现error:An incompatible version [1.1.32] of the APR based Apache Tomcat Native lib ...

  8. SpringBoot内置Tomcat启动不了的原因

    SpringBoot内置Tomcat启动不了的原因: 1.需要加入spring-boot-starter-web依赖 [web中集成了tomcat.dispatcherServlet.xml-] &l ...

  9. SpringBoot内置Tomcat浅析

    一.SpringBoot框架内置Tomcat,开发非常方便,随着SpringBoot的框架升级,内置Tomcat也更新版本.本文SpringBoot框架版本:2.2.10. 1.如何查看SpringB ...

最新文章

  1. ORA-01747: user.table.column, table.column 或列说明无效 异常解决方法总结
  2. TCP/IP详解--学习笔记(5)-IP选路,动态选路,和一些细节
  3. Sql自动更新不同IP的数据库数据。(link Server)
  4. go语言 不支持动态加载_动态语言支持
  5. linux qt 音频文件怎么打开,Qt:获取Linux中可用音频设备的列表
  6. vue_ form表单 v-model
  7. JavaEE Tutorials (13) - 使用锁定控制对实体数据的并发访问
  8. 95-242-040-源码-快照-Flink 分布式快照的设计-存储
  9. 解决数据库导入导出的常见问题集解决办法
  10. 关于telnet的问题
  11. [转贴]从零开始学C++之STL(二):实现一个简单容器模板类Vec(模仿VC6.0 中 vector 的实现、vector 的容量capacity 增长问题)...
  12. asp.net Json序列化
  13. python shelve模块_python中的Shelve模块不工作:“无法确定db type”
  14. 微博html5版登录网址,微博网页版登录入口
  15. mac php71 php fpm,Mac PHP-fpm
  16. xp外观主题下载_增强Windows XP外观的简便方法
  17. 淘东电商项目(68) -互联网安全架构设计(黑名单拦截及MD5加签)
  18. Ubuntu制作本地源
  19. 70张让你大开眼界的照片(配…
  20. 计算某年某月某日是该年中的第几天

热门文章

  1. 焕然一新,swagger UI 主题更改
  2. 【深度之眼吴恩达机器学习第四期】笔记(十一)
  3. 【板栗糖GIS】——如何使用插件将微信读书笔记同步到notion
  4. ViBe 背景提取算法原理
  5. input标签的tabindex属性 a标签的tabindex属性
  6. c语言库里的排序函数,C语言标准库函数qsort详解
  7. Individual household electric power consumption个人家庭用电量数据挖掘与时序预测建模
  8. 基于隐马尔科夫模型的道路匹配
  9. Cisco交换机密码设置
  10. Android音视频【十三】OpenSL ES介绍基于OpenSL ES实现音频采集