热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

这篇文章主要为大家介绍了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 {
    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 -> this.getCorePoolSize());
        createGauge(this, "grpc_tomcat_maximum_pool_size", pool -> this.getMaximumPoolSize());
        createGauge(this, "grpc_tomcat_busy_pool_size", pool -> this.getActiveCount());
        createGauge(this, "grpc_tomcat_wait_queue_size", pool -> this.getWaitQueueSize());
    } catch (Exception e) {
        log.error("注册 all servers 监控失败", e);
    }
}
private void createGauge(AllServersThreadPoolCollector weakRef, String metric, ToDoubleFunction 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 集群监控弹性伸缩的资料请关注其它相关文章!


推荐阅读
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 标题: ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Java 11相对于Java 8,OptaPlanner性能提升有多大?
    本文通过基准测试比较了Java 11和Java 8对OptaPlanner的性能提升。测试结果表明,在相同的硬件环境下,Java 11相对于Java 8在垃圾回收方面表现更好,从而提升了OptaPlanner的性能。 ... [详细]
  • 大数据Hadoop生态(20)MapReduce框架原理OutputFormat的开发笔记
    本文介绍了大数据Hadoop生态(20)MapReduce框架原理OutputFormat的开发笔记,包括outputFormat接口实现类、自定义outputFormat步骤和案例。案例中将包含nty的日志输出到nty.log文件,其他日志输出到other.log文件。同时提供了一些相关网址供参考。 ... [详细]
  • 本文介绍了禅道作为一款国产开源免费的测试管理工具的特点和功能,并提供了禅道的搭建和调试方法。禅道是一款B/S结构的项目管理工具,可以实现组织管理、后台管理、产品管理、项目管理和测试管理等功能。同时,本文还介绍了其他软件测试相关工具,如功能自动化工具和性能自动化工具,以及白盒测试工具的使用。通过本文的阅读,读者可以了解禅道的基本使用方法和优势,从而更好地进行测试管理工作。 ... [详细]
  • 本文介绍了解决java开源项目apache commons email简单使用报错的方法,包括使用正确的JAR包和正确的代码配置,以及相关参数的设置。详细介绍了如何使用apache commons email发送邮件。 ... [详细]
  • 项目运行环境配置及可行性分析
    本文介绍了项目运行环境配置的要求,包括Jdk1.8、Tomcat7.0、Mysql、HBuilderX等工具的使用。同时对项目的技术可行性、操作可行性、经济可行性、时间可行性和法律可行性进行了分析。通过对数据库的设计和功能模块的设计,确保系统的完整性和安全性。在系统登录、系统功能模块、管理员功能模块等方面进行了详细的介绍和展示。最后提供了JAVA毕设帮助、指导、源码分享和调试部署的服务。 ... [详细]
  • 本文介绍了在Mac上搭建php环境后无法使用localhost连接mysql的问题,并通过将localhost替换为127.0.0.1或本机IP解决了该问题。文章解释了localhost和127.0.0.1的区别,指出了使用socket方式连接导致连接失败的原因。此外,还提供了相关链接供读者深入了解。 ... [详细]
  • Whatsthedifferencebetweento_aandto_ary?to_a和to_ary有什么区别? ... [详细]
  • Activiti7流程定义开发笔记
    本文介绍了Activiti7流程定义的开发笔记,包括流程定义的概念、使用activiti-explorer和activiti-eclipse-designer进行建模的方式,以及生成流程图的方法。还介绍了流程定义部署的概念和步骤,包括将bpmn和png文件添加部署到activiti数据库中的方法,以及使用ZIP包进行部署的方式。同时还提到了activiti.cfg.xml文件的作用。 ... [详细]
  • 本文整理了Java中java.lang.NoSuchMethodError.getMessage()方法的一些代码示例,展示了NoSuchMethodErr ... [详细]
  • 本文总结了初学者在使用dubbo设计架构过程中遇到的问题,并提供了相应的解决方法。问题包括传输字节流限制、分布式事务、序列化、多点部署、zk端口冲突、服务失败请求3次机制以及启动时检查。通过解决这些问题,初学者能够更好地理解和应用dubbo设计架构。 ... [详细]
author-avatar
Li修贤_594
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有