Tomcat线程池监控及线程池原理分析

SpringBoot 内置 Tomcat 线程数优化配置 本文解析springboot内置tomcat调优并发线程数的一些参数,并结合源码进行分析。tomcat主要通过使用自己的TaskQueue队列来对线程池做出了不同的策略,也就是tomcat当线程数大于核心数时就会直接创建新的线程来处理,而不是放到队列。 阅读详情

  目录

        一、背景

        二、tomcat线程池监控

        三、tomcat线程池原理

        四、总结


一、背景

我们都知道稳定性、高可用对于一个系统来讲是非常重要的,而为了保证系统的稳定性,我们一般都会进行各方面的监控,以便系统有任何异常情况时,开发人员能够及时的感知到,这些监控比如缓存服务redis的监控,数据库服务mysql的监控,系统流量监控,系统jvm监控等等,除了这些监控,还有一种监控也是很有必要的,那就是线程池的监控。

说起线程池的监控可能我们一般想到的是我们自定义的线程池或者接入的中间件比如hystrix的线程池监控,但是其实还有一个线程池其实一直伴随着我们的开发生涯,日日用而不知,那就是SpringBoot内嵌Tomcat的线程池。

今天,这篇文章就来介绍SpringBoot内嵌Tomcat线程池监控及Tomcat的线程池原理分析。

二、tomcat线程池监控

既然我们要监控Tomcat的线程池,那么很自然的思路就是我们怎么获取到Tomcat的线程池对象,如果我们能够获取到Tomcat的线程池对象,那么,线程池的各项指标信息我们就能获取了。

如果我们想看Tomcat使用的线程池,那么正常的做法就是看源码了,跟随SpringApplication.run(AppApplication.class, args)启动方法一路进行源码跟踪。

在这里我就不一一跟踪源码进行讲解了,感兴趣的同学可以自己动手调试下,这里我分享一个Tomcat的架构图,这对于跟踪 Tomcat的源码非常有帮助。

Tomcat的源码也都是基于架构一部分一部分进行实现的。

在这里我就直接给出答案了:

①、Tomcat线程池的创建是在AbstractEndpoint这个抽象类中执行的。

也就是下面这段源码:

AbstractEndpoint#createExecutor()
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);
}

②、Tomcat的Servlet容器实现了WebServerApplicationContext或者ApplicationContext这个接口,所以我们注入WebServerApplicationContext或者直接注入ApplicationContext就能获取到Tomcat线程池对象。

如下所示:

代码如下:​​​​​​​

//获取webServer线程池
ThreadPoolExecutor executor = (ThreadPoolExecutor) ((TomcatWebServer) webServerApplicationContext.getWebServer())
        .getTomcat()
        .getConnector()
        .getProtocolHandler()
        .getExecutor();

好了,到这里我们就获取到Tomcat线程池对象了,有了线程池对象我们就可以对其进行监控,定时获取其监控指标,以便在服务异常时能告警通知。

这里我再简单介绍下获取到的Tomcat线程池对象ThreadPoolExecutor executor的一些指标意义。

其各项监控指标如下:

//获取webServer线程池
ThreadPoolExecutor executor = (ThreadPoolExecutor) ((TomcatWebServer) webServerApplicationContext.getWebServer())
        .getTomcat()
        .getConnector()
        .getProtocolHandler()
        .getExecutor();
Map<String, String> returnMap = new LinkedHashMap<>();
returnMap.put("核心线程数", String.valueOf(executor.getCorePoolSize()));
returnMap.put("最大线程数", String.valueOf(executor.getMaximumPoolSize()));
returnMap.put("活跃线程数", String.valueOf(executor.getActiveCount()));
returnMap.put("池中当前线程数", String.valueOf(executor.getPoolSize()));
returnMap.put("历史最大线程数", String.valueOf(executor.getLargestPoolSize()));
returnMap.put("线程允许空闲时间/s", String.valueOf(executor.getKeepAliveTime(TimeUnit.SECONDS)));
returnMap.put("核心线程数是否允许被回收", String.valueOf(executor.allowsCoreThreadTimeOut()));
returnMap.put("提交任务总数", String.valueOf(executor.getSubmittedCount()));
returnMap.put("历史执行任务的总数(近似值)", String.valueOf(executor.getTaskCount()));
returnMap.put("历史完成任务的总数(近似值)", String.valueOf(executor.getCompletedTaskCount()));
returnMap.put("工作队列任务数量", String.valueOf(executor.getQueue().size()));
returnMap.put("拒绝策略", executor.getRejectedExecutionHandler().getClass().getSimpleName());

三、tomcat线程池原理

在上面介绍了获取到的Tomcat线程池对象ThreadPoolExecutor executor,我们一看这个线程池类,竟然是ThreadPoolExecutor ,难道就是JUC并发包中的ThreadPoolExecutor ?聪明的我赶紧看看Tomcat的源码,非也非也,原来这个ThreadPoolExecutor 是Tomcat扩展了java.util.concurrent.ThreadPoolExecutor,Tomcat根据自己独特的业务场景定制实现的一个线程池。

如下图

其实如果看下这个org.apache.tomcat.util.threads包里面的ThreadPoolExecutor的实现的话,我们会惊奇的发现,这个org.apache.tomcat.util.threads包里面的ThreadPoolExecutor和java.util.concurrent.ThreadPoolExecutor的实现大致都是相同的,在这里,我就详细介绍下两个ThreadPoolExecutor在执行具体的任务时是怎么实现的,有什么区别。

在这里先列出两个ThreadPoolExecutor的执行逻辑

org.apache.tomcat.util.threads包里面的ThreadPoolExecutor​​​​​​​

public void execute(Runnable command, long timeout, TimeUnit unit) {
    submittedCount.incrementAndGet();
    try {
        executeInternal(command);
    } catch (RejectedExecutionException rx) {
        if (getQueue() instanceof TaskQueue) {
            // If the Executor is close to maximum pool size, concurrent
            // calls to execute() may result (due to Tomcat's use of
            // TaskQueue) in some tasks being rejected rather than queued.
            // If this happens, add them to the queue.
            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();
  }
  /*
   * Proceed in 3 steps:
   *
   * 1. If fewer than corePoolSize threads are running, try to
   * start a new thread with the given command as its first
   * task.  The call to addWorker atomically checks runState and
   * workerCount, and so prevents false alarms that would add
   * threads when it shouldn't, by returning false.
   *
   * 2. If a task can be successfully queued, then we still need
   * to double-check whether we should have added a thread
   * (because existing ones died since last checking) or that
   * the pool shut down since entry into this method. So we
   * recheck state and if necessary roll back the enqueuing if
   * stopped, or start a new thread if there are none.
   *
   * 3. If we cannot queue task, then we try to add a new
   * thread.  If it fails, we know we are shut down or saturated
   * and so reject the task.
   */
  int c = ctl.get();
  if (workerCountOf(c) < corePoolSize) {
      if (addWorker(command, true)) {
          return;
      }
      c = ctl.get();
  }
  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);
      }
  }
  else if (!addWorker(command, false)) {
      reject(command);
  }
}

java.util.concurrent.ThreadPoolExecutor​​​​​​​

public void execute(Runnable command) {
  if (command == null)
      throw new NullPointerException();
  /*
   * Proceed in 3 steps:
   *
   * 1. If fewer than corePoolSize threads are running, try to
   * start a new thread with the given command as its first
   * task.  The call to addWorker atomically checks runState and
   * workerCount, and so prevents false alarms that would add
   * threads when it shouldn't, by returning false.
   *
   * 2. If a task can be successfully queued, then we still need
   * to double-check whether we should have added a thread
   * (because existing ones died since last checking) or that
   * the pool shut down since entry into this method. So we
   * recheck state and if necessary roll back the enqueuing if
   * stopped, or start a new thread if there are none.
   *
   * 3. If we cannot queue task, then we try to add a new
   * thread.  If it fails, we know we are shut down or saturated
   * and so reject the task.
   */
  int c = ctl.get();
  if (workerCountOf(c) < corePoolSize) {
      if (addWorker(command, true))
          return;
      c = ctl.get();
  }
  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);
  }
  else if (!addWorker(command, false))
      reject(command);
}

相较于JDK 自带的ThreadPoolExecutor,上面多了 submittedCount.incrementAndGet() 和 catch 异常之后的那部分代码。

submittedCount,是一个 AtomicInteger ,意义是已提交但尚未完成的任务数,这包括队列中的任务和已交给工作线程但尚未执行完成的任务。catch 中的代码很好理解,作用是让被拒绝的请求再次加入到队列中,尽力处理任务。

然后再来看 executeInternal 方法,其实你会发现executeInternal 方法的执行逻辑和java.util.concurrent.ThreadPoolExecutor的execute()执行逻辑竟然完全相同,这令我们很迷惑,难道直接就是复用的java.util.concurrent.ThreadPoolExecutor的执行逻辑,假如说直接就是复用的java.util.concurrent.ThreadPoolExecutor的执行逻辑,那么直接super.execute()不就完了,还有必要重写一遍代码吗?

这个时候我们就要回到之前创建Tomcat的线程池的现场,看看创建线程池的时候和JUC里面到底有哪些不一样,因为看代码他们execute()执行逻辑完全一样,那肯定是具体执行的时候有些实现不一样。否则,Tomcat的开发者是绝对不会笨到重写一遍java.util.concurrent.ThreadPoolExecutor的执行逻辑的。

重新来到Tomcat创建线程池的时机,也即是下面这段代码。​​​​​​​

AbstractEndpoint#createExecutor()
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);
}

我们看ThreadPoolExecutor构造方法的参数,核心线程数、最大线程数这些没有太大意义就不用看了,重点关注taskqueue这个参数。

taskqueue是Tomcat根据自身的独特业务场景逻辑实现了阻塞队列LinkedBlockingQueue<Runnable>,然后我们结合之前Tomcat的execute()逻辑,看在execute()里面,taskqueue是怎么执行的。

也即是下面这段代码​​​​​​​

@Override
public boolean offer(Runnable o) {
  //we can't do any checks
    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
    if (parent.getPoolSize()<parent.getMaximumPoolSize()) {
        return false;
    }
    //if we reached here, we need to add it to the queue
    return super.offer(o);
}

首先如果parent 为 null,直接入队,实际上这个 parent 就是ThreadPoolExecutor,在刚才Tomcat创建线程池的地方有这个代码。

然后,parent.getPoolSize() 的作用是返回当前线程池中的线程数,如果等于最大线程数,则直接入队,等待后续执行这个任务。

然后parent.getSubmittedCount()标识提交的任务数量,如果小于等于线程数量,标识有空闲的线程在等待任务,所以这个时候也是直接入队,让空闲线程立即去执行任务,

再然后,parent.getPoolSize()<parent.getMaximumPoolSize()表示线程池中的线程数量如果小于限制的最大线程数量,那么这个时候就强制开启新线程,去执行任务。这个时候就是返回false,我们看Tomcat的ThreadPoolExecutor的execute()在taskqueue.offer()方法,返回false了,就去执行addWorker()开启新线程了。

最后,执行到最后,默认就直接入队了。

好了,这就是Tomcat的线程池执行所有逻辑了,这个时候我们再反过来去看java.util.concurrent.ThreadPoolExecutor的execute()方法,虽然他的执行逻辑和Tomcat的org.apache.tomcat.util.threads包里面的ThreadPoolExecutor的execute()方法执行逻辑表面完全相同,但是他在执行taskqueue.offer()的时候,其实是直接执行的LinkedBlockingQueue或者其他阻塞队列的逻辑,直接入队了,这就是Tomcat的线程池和JUC线程池最大的一点不同。

四、总结

①、Tomcat线程池的创建是在AbstractEndpoint这个抽象类中执行的。

②、注入WebServerApplicationContext或者直接注入ApplicationContext就能获取到Tomcat线程池对象。

③、当有新任务时,Tomcat的线程池核心线程如果已经创建完了,Tomcat会尽最大努力开启新的非核心线程去执行新任务,而JUC的ThreadPoolExecutor则是入队,等待队列满了再创建新的非核心线程去执行任务。

以上是个人的亲身经历及总结经验,个人之见,难免考虑不全,如果大家有更好的建议欢迎大家私信留言。

如果觉得对你有一点点帮助,希望能够动动小手,你的点赞是对我最大的鼓励支持。

更多分享请移步至个人公众号,谢谢支持😜😜......

公众号:wenyixicodedog  

性能测试 —— Tomcat监控与调优:Jconsole监控 VM摘要的监控信息如图10-11所示。图10-11 VM摘要信息摘要信息、线程和类信息、内存信息、操作系统信息和其它信息。连接名称:连接监控时的进程PID信息;运行时间:开始以来Java虚拟机运行的时间总额;处理CPU时间:Java VM的开始,消耗的CPU时间总量;编译总时间:累计时间花费在JIT编译所花费的时间;活动线程:当前活动的线程;峰值:最大线程数;守护线程:即运行在后台的线程;已启动的线程总数:运行到目前为止共启动的线程数; 阅读详情

相关推荐

SpringBoot内置Tomcat性能调优实战:从参数解析到压测验证

在Java Web开发中,Servlet容器是承载应用运行的核心组件,负责处理HTTP请求与响应。其工作原理基于多线程模型和连接池机制,通过优化线程调度与连接管理来提升并发处理能力。这项技术的价值在于能够显著提高应用吞吐量、降低响应延迟,是构建高可用分布式系统的关键环节。常见的应用场景包括电商大促、实时数据处理和高并发API服务等。本文聚焦于SpringBoot内置Tomcat的调优实践,深入解析max-threads、accept-count等核心参数配置,并结合连接池优化和内存管理策略,帮助开发者解决实

weixin_34014555的博客 342

Tomcat线程池监控和压测

Tomcat提供了一个管理界面,通过浏览器访问特定的URL(如http://localhost:8080/manager/status),可以查看Tomcat的运行状态,包括线程池的使用情况。通过合理的监控和压测方法,可以全面了解Tomcat的性能状况,及时发现并解决性能瓶颈,为Web应用的稳定运行提供有力保障。还可以使用一些第三方的监控工具,如AppDynamics、New Relic等,这些工具集成了对Tomcat性能的监控和分析功能,可以提供更加详细的监控和报警信息。一、Tomcat线程池监控。

猿脑2.0的博客 933

SpringBoot实战:精准防御Slow HTTP慢速攻击的配置策略

本文详细解析了Slow HTTP慢速攻击的原理与危害,并提供了SpringBoot实战配置策略,包括连接超时设置、线程池调优及全链路防御方案。通过精准配置`connection-timeout`等参数,有效防御慢速攻击,保障服务器资源安全。

weixin_33748818的博客 449

Tomcat 线程池

Tomcat 是一个流行的 Java Web 服务器,它使用线程池来处理客户端请求。线程池是一组预先创建的线程,用于执行并发任务,以提高性能和资源利用率。在 Tomcat 中,线程池被用来处理传入的 HTTP 请求。当客户端发送请求时,Tomcat 会从线程池中获取一个可用的线程来处理该请求。处理完请求后,线程将返回线程池,并在下一个请求到来时再次被重用。Tomcat 的线程池配置可以通过编辑server.xml文件来进行。maxThreads:指定线程池中的最大线程数。

一个菜鸡的博客 4245

【Java生产级避坑指南】8. Tomcat线程池下的内存地雷:ThreadLocal泄漏检测与实战解决

摘要:某金融交易系统(Spring Boot 2.7 + Tomcat 9)在线上运行时出现严重内存泄漏:堆内存(4GB)72小时内耗尽并触发OOM,日均200万请求场景下,Full GC频率从正常1次/天飙升至6次/小时。排查发现,根源是ThreadLocal未清理——Tomcat线程池复用线程时,UserInfo等大对象被ThreadLocalMap强引用,无法被GC回收。本文详细讲解ThreadLocal内存泄漏原理,提供Arthas实时诊断、MAT内存分析、自定义监控端点3种实操检测方法。

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 1252

深入后端领域,了解 Tomcat 的线程池管理

资源限制:如何平衡线程数与CPU、内存的关系?性能权衡:如何在吞吐量(每秒处理请求数)与延迟(请求响应时间)之间找到最优解?过载保护:当请求量超过系统容量时,如何避免服务崩溃?动态适配:如何应对请求量的波动(如电商大促、秒杀活动)?Tomcat线程池是后端服务性能优化的核心组件,其设计涵盖了理论框架架构设计实现机制实际应用等多个维度。通过理解其底层逻辑与优化策略,开发者可以构建高并发、高可用的后端服务,应对日益增长的业务需求。

AI架构全栈开发实战笔记 298

Tomcat线程池梳理

最大连接数:每个Http请求占用一个连接Http请求数超过最大连接数时,多出的请求会放入等待队列,如果队列也满了请求会被丢弃。所以Tomcat能接收的请求数为最大连接数+等待队列长度。Tomcat最多能同时处理的请求数,比如有30个请求,但是最大线程数为20,那么同时有20个请求可以被处理。

LuckFairyLuckBaby的博客 1664

LLM - windows下的Dify离线部署:从镜像打包到无网环境部署(亲测,包含插件部署)

LLM - windows下的Dify离线部署:从镜像打包到无网环境部署(亲测,包含插件部署)

ocean00008899的博客 1285

Tomcat专题

一、目录结构: 1、webAPP:代码工程部署目录(war包是对外提供服务,jar一般是内部使用的) 2、bin *.bat(Windows) *.sh(Linux) catalina.sh(jvm) 3、logs日志文件 运行日志:catalina.out;用户访问日志:localhost_access_log.201x-12-10.txt 4、lib:tomcat使用的jar 5、...

村东头老杨的博客 190

Android TV开发:RecyclerView焦点控制实践与优化

在Android TV应用开发中,焦点控制是提升用户体验的关键技术。不同于移动端触摸交互,TV端通过遥控器方向键导航,需要精确的焦点管理机制。RecyclerView作为动态列表的核心组件,其焦点控制涉及视图复用、导航逻辑和视觉反馈等核心技术点。通过合理配置focusable属性、自定义焦点查找算法以及优化动画性能,开发者可以解决TV端常见的焦点跳转异常、视觉反馈延迟等问题。特别是在视频点播、智能家居控制等场景中,良好的焦点控制能显著提升操作效率。本文结合网格布局焦点环绕、分页加载焦点保持等实战案例,深入解

weixin_34195142的博客 469

【数据挖掘】遗传算法原理浅析:什么是遗传算法?遗传算法的工作原理是什么?

遗传算法(Genetic Algorithm,GA),是进化算法的框架和核心。 各种进化算法有什么异同? - 及时物语的回答 - 知乎 https://www.zhihu.com/question/39152390/answer/901070749

纸羊同学的博客 4271

学习日报|线程池专题学习总结

线程池是复用线程的容器,通过预先创建线程降低创建/销毁成本。核心参数包括线程数、队列、拒绝策略等。执行流程遵循"核心→队列→扩容→拒绝"四步,状态包含RUNNING至TERMINATED。关键优化点:使用有界队列、合理配置线程数、设置任务超时、监控活跃线程/拒绝数。常见OOM场景包括无界队列堆积、任务泄漏等。最佳实践为:有界队列+显式拒绝+分池隔离+监控告警。注意避免无界队列和无超时调用,通过限流、降级等机制保障系统稳定。

一條狗的博客 957

数字滤波器应用介绍

线性滤波器FIR ,会引起恒定延迟,通过补零的方式补偿非线性滤波器IIR,会引起频率相关的延迟,通过零相位延迟filtfilt补偿从信号中去除不需要的频谱时,通过滤波和补偿实现。如果需要去除的频率与采样率相差甚远,比如4kHz 与100Hz, 可通过 低通滤波->补偿->降采样–> 滤波 补偿 -->上采样还原的方法。*降采样前一定要先低通滤波为了防止混叠。

weixin_39107270的博客 274

java-进程和线程原理及应用

前言 进程和线程作为操作系统的基本对象,理解它们有助于更好的编写可靠高效的代码。 进程 一个程序在操作系统中执行后,运行起来的单元就是进程。一个程序可以多次执行,运行出多个进程。比如google浏览器,每次执行都会打开一个进程,这个进程为用户提供浏览网页服务。 线程 操作系统最小的执行单元,也是操作系统任务调度的最小单元。 线程的创建 new,Runnable,ExecutorService 线程的信息 线程的信息可以通过java.lang.Thread类获取。 线程的优先级 线程是由操作系统调度的,而操作

ssehs的博客 3995

深度学习驱动的股票波动率预测:LSTM和Transformer模型的实战解析

股票市场的波动率一直是金融领域关注的焦点之一。波动率预测对于风险管理、期权定价和资产配置至关重要。深度学习模型,如长短时记忆网络(LSTM)和Transformer,已经在时间序列数据分析领域取得了显著的成就。本博客将向您展示如何使用这两种深度学习模型来进行股票波动率预测,提供清晰的思路和相应的Python代码,以帮助您理解和应用这一重要任务。

斌擎科技 974

计算机组成原理(第三版)唐朔飞-第六章计算机的运算方法-课后习题(17-32)

计算机组成原理(第三版)唐朔飞-第六章计算机的运算方法-课后习题(17-32)

weixin_45735391的博客 12万+

如何合理设置线程池参数?Java 线程池调优完全指南

【摘要】线程池参数配置需根据任务类型(CPU/IO密集型)动态调整:CPU密集型建议核心线程数设为CPU核数+1,使用同步队列;IO密集型可扩大线程数至CPU核数*2,配合大容量阻塞队列。关键参数包括核心/最大线程数、存活时间、队列类型及拒绝策略,需关注其协作关系——当核心线程满载后任务入队,队列满则扩容至最大线程数,最终触发拒绝策略。主流框架(如Spring、Tomcat)均有线程池实践,推荐手动创建而非Executors工具类以避免OOM风险。面试需掌握参数设定逻辑、队列选择及优雅关闭方法,并理解线程数

m0_66083353的博客 724

SAP VC 可配置物料超级BOM简介

在有些业务中比如:汽车、笔记本、电脑、电梯等产品中,其中组成产品的型号个框架是确认的 ,但是框架内部的BOM是可变的,在客户下销售订单的时候,选取对应的组件 ,让后通过MRP的计算优生产线对这样的销售订单提供生产。这个时候我们需要选择我们需要的内存和硬盘的型号,还记得我们前面创建的特征值对应的吗,A对应的8G、B对应16G、X对应1T硬盘、Y对应2T硬盘,这里我们填写对应的值,我们这里选择16G的内存和2T的硬盘,B和Y。3、当我们选择了可配置物料的生产模式时,基本上就选择了按单生产的生产模式了。

我小时候很黑的博客 1万+

Tomcat与Servlet容器核心原理与优化实践

Servlet容器是Java Web应用运行的核心环境,负责处理HTTP请求的生命周期管理、线程调度和会话维护。作为开源实现的Tomcat通过分层容器架构和模块化设计,实现了高效的请求处理机制。其核心组件包括连接器(Connector)处理网络通信,容器(Container)管理Servlet生命周期,以及管道(Pipeline)实现处理流程的可扩展性。在性能优化方面,合理配置线程池参数、选择适当的IO模型(如NIO/APR)以及JVM调优能显著提升吞吐量。结合热部署、异步处理等特性,Tomcat广泛应用于

weixin_30768881的博客 38

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

ThreadLocal内存泄漏深度解析:从弱引用原理到线上OOM实战排查

在Java并发编程中,ThreadLocal是解决线程隔离数据存储的核心工具,其底层基于ThreadLocalMap实现。理解其内存管理机制,关键在于掌握弱引用与强引用的交互原理:Entry的Key使用弱引用指向ThreadLocal实例,而Value保持强引用。这一设计在特定场景下会引发技术风险——当ThreadLocal外部强引用消失且线程长期存活时,Key被GC回收,Value却因线程强引用而无法释放,导致内存中积累大量不可达的脏Entry。这种内存无效占用问题在Web服务器、线程池等高并发工程实践中

weixin_34174322的博客 312

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Spring Boot线程模型解析与性能优化实践

线程模型是构建高并发系统的核心基础,Spring Boot默认采用thread-per-request同步阻塞模型,通过线程池处理HTTP请求。这种模型在I/O密集型场景下会出现线程阻塞问题,导致吞吐量下降。理解Tomcat线程池配置、异步处理原理以及响应式编程技术,可以帮助开发者优化系统性能。实际工程中需要根据业务场景选择合适方案,结合Servlet异步处理、WebFlux等技术解决性能瓶颈。通过线程转储分析和参数调优,可以有效提升系统吞吐量,应对高并发挑战。

weixin_30899023的博客 67

Spring Boot嵌入式Web容器选型与性能优化指南

嵌入式Web容器是现代Java应用开发的核心组件,它通过将服务器运行时集成到应用中,实现了独立部署和快速启动。其工作原理是基于Servlet规范,通过内嵌的HTTP引擎处理网络请求。这种架构显著提升了微服务场景下的部署效率,相比传统WAR部署节省了50%以上的启动时间。在技术实现上,主流方案包括Tomcat、Jetty、Undertow和Netty,各自针对不同场景优化:Tomcat适合传统MVC应用,Undertow在高并发API场景表现突出,而Netty则是响应式编程的首选。开发者在实际选型时,需要综合

weixin_29300931的博客 181

Tomcat性能优化与故障排查:高频面试题与实战经验

在Java Web应用部署中,Tomcat是核心的Servlet容器与HTTP服务器。理解其架构、线程模型及请求处理链路,是进行有效性能调优与故障诊断的基础。通过分析Connector与Container的分工、NIO模型、线程池参数联动关系,以及JVM堆内存与GC设置,开发者能系统掌握中间件优化方法。实际生产环境中,连接数爆满、OOM内存溢出、启动失败等高频问题往往源于下游依赖或配置不合理。借助jstack、jmap等工具定位瓶颈,从应用代码到数据库连接池逐层排查,能够避免“盲调参数”带来的雪崩风险。本文

weixin_31742871的博客 167

如何监控Tomcat的性能?

Tomcat 的关键指标 Tomcat 的关键指标有吞吐量、响应时间、错误数、线程池、CPU 以及 JVM 内存。 我来简单介绍一下这些指标背后的意义。其中前三个指标是我们最关心的业务指标,Tomcat 作为服务器,就是要能够又快有好地处理请求,因此吞吐量要大、响应时间要短,并且错误数要少。 而后面三个指标是跟系统资源有关的,当某个资源出现瓶颈就会影响前面的业务指标,比如线程池中的线程数量不足会影响吞吐量和响应时间;但是线程数太多会耗费大量 CPU,也会影响吞吐量;当内存不足时会触发频繁地 GC,..

u012154840的博客 2293

Java 性能 08 - Tomcat 线程池优化(maxThreads/minSpareThreads)

摘要:Tomcat线程池中的maxThreads和minSpareThreads是高并发Web应用的关键性能参数。maxThreads限制最大线程数(默认200),防止资源耗尽;minSpareThreads保持最小空闲线程(默认10),减少创建开销。优化建议: CPU密集型:maxThreads接近核心数 I/O密集型:可设较高(200-500) 提升minSpareThreads(如50)预热线程,减少延迟 测试显示平衡配置(max=200, min=50)比低配响应快30%,比高配更节省资源。需结合业

千淘万漉虽辛苦,吹尽狂沙始到金 2万+
上一篇: HystrixCommand使用及创建线程池问题
下一篇: 美团Cat监控集成Hystrix线程池链路分析
wenyixicodedog
博客等级 码龄8年 87粉丝 116原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值