tomcat 集群监控与弹性伸缩详解

 更新时间:2022年09月09日 08:37:07   作者:随风21  
这篇文章主要为大家介绍了tomcat 集群监控与弹性伸缩详解,

如何给 tomcat 配置合适的线程池

任务分为 CPU 密集型和 IO 密集型

对于 CPU 密集型的应用来说,需要大量 CPU 计算速度很快,线程池如果过多,则保存和切换上下文开销过高反而会影响性能,可以适当将线程数量调小一些

对于 IO 密集型应用来说常见于普通的业务系统,比如会去查询 mysql、redis 等然后在内存中做简单的数据组装和逻辑判断,此时由于有 epoll、dma 等支持,消耗较长时间的线程不会长时间占据 CPU 时间,所以可以适当将线程数量调大一些

对于线程数到底该设置多大,网上有很多的方案

我们在实际情况中基于 wrk 等压测工具进行压测,压测逻辑相对复杂请求量相对较高的接口先配置一个相对保守的值

先预估假设当前接口处理平均耗时 300MS,1S 一个线程平均处理 3 个请求,最大 100 个线程 1S 能处理 300 TPS

粗略估算每个请求会消耗多大的内容空间,假如是 2KB,那么每秒会消耗 600KB 以此来估算 YGC 多久会触发一次,假设年轻代为 1GB 那么大约 29 分钟触发一次 YGC

然后再看 100 个线程持续处理 300TPS 的时候 CPU 消耗情况

观察内存几分钟 GC 一次,或者 CPU 使用率稳定在 50% 左右就好,假设此时线程池 70 个线程都在运作

那么监控系统采集到线程池线程使用率达到了 80% 就开始扩容,因为扩容拉起新的服务器到提供服务可能也需要1-2分钟的时间,所以需要预留服务器资源能够处理弹性扩容期间的流量

实际场景中是相对比较复杂的,前期最好设置一个相对保守的扩容阈值,如果发现在达到这个阈值前服务器已经顶不住了就可以实时的缩小阈值

同时还可以根据在达到扩容阈值扩容的时候,观察线上真实的 CPU 和内存使用情况,可以基于 promethues + grafana 来进行监控,如果发现扩容时候 CPU 使用率和内存使用率 GC 评率比较低,那么可以再配置中心动态的调整线程池的大小

所以说与其寻找一个准确的计算线程池数量的配置方式,不如提供一个可以动态调整的线程池作为 tomcat 的线程池

如何监控 tomcat 线程池的工作情况

spring boot 在启动过程中会调用 TomcatProtocolHandlerCustomizer 的实现类,此处可以自定化 tomcat 线程池,也可以获取到 tomcat 线程池

public class MyTomcatProtocolHandlerCustomizer implements TomcatProtocolHandlerCustomizer<ProtocolHandler> {
    private final ThreadPoolExecutor tomcatThreadPoolExecutor;
    public TomcatProtocolHandlerCustomizer(ThreadPoolExecutor tomcatThreadPoolExecutor) {
        this.tomcatThreadPoolExecutor = tomcatThreadPoolExecutor;
    }
    @Override
    public void customize(ProtocolHandler protocolHandler) {
        protocolHandler.setExecutor(tomcatThreadPoolExecutor);
    }
    public ThreadPoolExecutor getThreadPoolExecutor() {
        return tomcatThreadPoolExecutor;
    }
}

然后将线程池装入一个容器 bean 中注册到 promethues 监控中,当每秒进行采集的时候通过回调的方法去获取线程池的最新指标

promethues 每秒采集一次容器的线程池运行指标、服务器测 CPU 使用率当满足定义的扩容阈值时就拉起新的 POD

grafana 每秒采集一次 promethues 的数据汇聚图标展示

@Bean
public AllServersThreadPoolCollector allServersThreadPoolCollector(@Qualifier(value = "GrpcThreadPoolExecutor") ThreadPoolExecutor GrpcThreadPoolExecutor,
                                                                   @Qualifier(value = "jdTomcatThreadPoolExecutor") org.apache.tomcat.util.threads.ThreadPoolExecutor TomcatThreadPoolExecutor,
                                                                   MeterRegistry registry) {
    return new AllServersThreadPoolCollector(GrpcThreadPoolExecutor, jdTomcatThreadPoolExecutor, registry);
}
public void init() {
    try {
        createGauge(this, "grpc_tomcat_core_pool_size", pool -&gt; this.getCorePoolSize());
        createGauge(this, "grpc_tomcat_maximum_pool_size", pool -&gt; this.getMaximumPoolSize());
        createGauge(this, "grpc_tomcat_busy_pool_size", pool -&gt; this.getActiveCount());
        createGauge(this, "grpc_tomcat_wait_queue_size", pool -&gt; this.getWaitQueueSize());
    } catch (Exception e) {
        log.error("注册 all servers 监控失败", e);
    }
}
private void createGauge(AllServersThreadPoolCollector weakRef, String metric, ToDoubleFunction&lt;AllServersThreadPoolCollector&gt; measure) {
    Gauge.builder(metric, weakRef, measure)
            .register(this.registry);
}
public int getWaitQueueSize() {
    return grpcThreadPoolExecutor.getQueue().size() + tomcatThreadPoolExecutor.getQueue().size();
}

tomcat 线程池扩缩容

Java 线程池是支持直接修改 corePoolSize、maximumPoolSize 的

  • 修改 corePoolSize

(1)数量小于 maximumPoolSize 抛异常

(2)如果 corePoolSize - 原来的 = delta,delta 大于 0 那么创建等待队列任务数量和 delta 个线程来处理等待处理的任务

(3)如果正在运行的线程数量 > corePoolSize 那么就中断多余的线程

  • 修改 maximumPoolSize

(1)maximumPoolSize 小于 corePoolSize 抛错

(2)如果运行的线程大于 maximumPoolSize 中断掉一些空闲的线程

基于这些机制就能在运行期间动态调整线程池内容

无需担心会中断掉正在运行的任务,因为线程池 worker 线程每执行一个任务的时候

tomcat 是如何避免原生线程池的缺陷的

原生线程池的工作原理

(1)运行的线程数小于核心线程,就创建一个 worker 线程去执行任务

(2)运行的线程数 >= 核心线程了,将任务全部积压到队列中

(3)队列如果满了继续创建非核心线程 worker 去执行任务

假如 tomcat 采用了原生线程池,核心线程为 10 个,最大线程为 100,队列为 200,并发来了 100 个请求,那么同时系统只能处理 10 个,剩下 90 个都得放入队列中让 10 个核心线程慢慢排队处理,延时必然非常高

tomcat 如何优化线程池,核心在于阻塞队列的实现,因为阻塞队列满了才会继续创建非核心 worker 线程处理任务

(1)运行的线程数小于核心线程,就创建一个 worker 线程去执行任务

(2)当前已经创建的核心+非核心线程数等于最大线程数,任务压入队列

(3)正在处理的请求数量小于核心+非核心线程数,任务压入队列

(4)当前已经创建的核心+非核心线程数小于最大线程数,创建 worker 线程处理请求

总结就是一句话当高并发流量过来的时候,会去创建最大线程数的 worker 去处理请求用以降低尾延迟,超过最大线程后,任务将被压入队列中进行处理

以上就是tomcat 集群监控与弹性伸缩详解的详细内容,更多关于tomcat 集群监控弹性伸缩的资料请关注脚本之家其它相关文章!

相关文章

  • Tomcat在linux环境中开机自启(定时重启)的方法

    Tomcat在linux环境中开机自启(定时重启)的方法

    我们经常会遇到服务器断电或异常,而异常后tomcat中部署的web项目需要我手动去启动,为此,特别贡献出Linux环境中Tomcat开机自启的方式供学习使用,需要的朋友可以参考下
    2023-10-10
  • 如何解决tomcat管理页面403 Access Denied问题

    如何解决tomcat管理页面403 Access Denied问题

    这篇文章主要介绍了如何解决tomcat管理页面403 Access Denied问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • 详解关于tomcat切割catalina.out日志的三种方式

    详解关于tomcat切割catalina.out日志的三种方式

    这篇文章主要介绍了详解关于tomcat切割catalina.out日志的三种方式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-02-02
  • 深度解析Tomcat 线程池与 JDK 线程池的区别和联系

    深度解析Tomcat 线程池与 JDK 线程池的区别和联系

    Tomcat 线程池和 JDK 线程池都是在 Java 开发中非常有用的工具,用于处理不同类型的并发任务,本文将深入探讨 Tomcat 线程池与JDK 线程池之间的区别和联系,以帮助开发人员更好地理解它们的工作原理和如何在自己的项目中使用它们
    2023-11-11
  • Tomcat获取Nginx反向代理的客户端域名

    Tomcat获取Nginx反向代理的客户端域名

    今天小编就为大家分享一篇关于Tomcat获取Nginx反向代理的客户端域名,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • 基于Tomcat 数据源的原理、配置、使用介绍

    基于Tomcat 数据源的原理、配置、使用介绍

    下面小编就为大家带来一篇基于Tomcat 数据源的原理、配置、使用介绍。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • 解决tomcat出现:java.lang.IllegalStateException:无输出目录问题

    解决tomcat出现:java.lang.IllegalStateException:无输出目录问题

    这篇文章主要介绍了解决tomcat出现:java.lang.IllegalStateException:无输出目录问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-01-01
  • tomcat配置虚拟路径的实现步骤

    tomcat配置虚拟路径的实现步骤

    本文主要介绍了tomcat配置虚拟路径的实现步骤,主要是在localhost文件中进行配置,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • Windows系统下安装Tomcat服务器和配置虚拟目录的方法

    Windows系统下安装Tomcat服务器和配置虚拟目录的方法

    这篇文章主要介绍了Windows系统下安装Tomcat服务器和配置虚拟目录的方法,Tomcat服务器用于驱动JSP和Servlet程序,需要的朋友可以参考下
    2015-12-12
  • startup.bat启动Tomcat闪退问题原因及解决

    startup.bat启动Tomcat闪退问题原因及解决

    本文主要介绍了startup.bat启动Tomcat闪退问题原因及解决,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04

最新评论