加入收藏 | 设为首页 | 会员中心 | 我要投稿 甘南站长网 (https://www.0941zz.com/)- 科技、行业物联网、开发、云计算、云管理!
当前位置: 首页 > 运营中心 > Tomcat > 正文

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

发布时间:2023-02-20 10:05:22 所属栏目:Tomcat 来源:互联网
导读:对于 IO 密集型应用来说常见于普通的业务系统,比如会去查询 mysql、redis 等然后在内存中做简单的数据组装和逻辑判断,此时由于有 epoll、dma 等支持,消耗较长时间的线程不会长时间占据 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 -> 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<AllServersThreadPoolCollector> 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 那么就中断多余的线程
 
 
 
  tomcat 是如何避免原生线程池的缺陷的
  原生线程池的工作原理
 
  (1)运行的线程数小于核心线程,就创建一个 worker 线程去执行任务
 
  (2)队列如果满了继续创建非核心线程 worker 去执行任务
 
  假如 tomcat 采用了原生线程池,核心线程为 10 个,最大线程为 100,队列为 200,并发来了 100 个请求,那么同时系统只能处理 10 个,剩下 90 个都得放入队列中让 10 个核心线程慢慢排队处理,延时必然非常高
 
  tomcat 如何优化线程池,核心在于阻塞队列的实现,因为阻塞队列满了才会继续创建非核心 worker 线程处理任务
 
  (1)运行的线程数小于核心线程,就创建一个 worker 线程去执行任务
 
  (2)当前已经创建的核心+非核心线程数等于最大线程数,任务压入队列
 
  (3)当前已经创建的核心+非核心线程数小于最大线程数,创建 worker 线程处理请求
 

(编辑:甘南站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读