明明CPU和内存负载不高,服务怎么就挂了呢?

399 阅读3分钟

1、背景:

随着云技术的发展,公司的各种系统、服务都迁移上云了。前段时间,生产有个服务每隔一段时间就告警,显示健康检查失败,服务被k8s强制重启,差点被领导请去办公室喝茶。

2、分析&排查

2.1、CPU和内存

当告警发生,第一时间上云平台观察服务监控指标状态,通过监控面板发现,CPU和内存的使用率并不高,甚至还有点低,所以排除了CPU和内存不足的可能。

图片.png

2.2、数据库连接池

服务的探活接口是基于简单sql的执行(select 1 from dual),并且本身服务的部分接口本身很慢,在流量大的情况下可能会占用jdbc连接,影响探活接口(本人其他服务曾遇到类似情况)。

基于上述分析,调大了服务的数据库连接池,但问题依旧存在,故排除此种可能。

2.3、工作线程池

既然不是资源问题,也不是数据库连接池问题,通过分析日志,除了发现响应时间很长(长于正常接口的响应时长)之外,并未发现有任何异常日志,会不会是undertow本身的问题呢?

默认情况下,undertow的工作线程是以系统获取的CPU个数为基础分配计算的,当CPU数与2取最大值=io-worker,测试了一下,容器内部获取CPU为1,则 io-worker =2。

而workerThreads= ioThreads * 8 ,实际值则为16,一个实例仅16个线程,明显无法满足需求,导致大量等待请求,最终被强制重启。

图片.png 从上述调整前的jvm线程使用情况来看,在高并发的情况下,这一点点线程数是不够用的,CPU都没跑满,并发一上来就直接堵塞,无法正常处理,资源浪费不说,还导致服务重启中断。

图片.png 在调整了undertow的工作线程数(io-threads: 8 worker-threads: 256)后,可以发现,jvm的线程数几乎增长了10倍,服务也不再告警,接口堵塞无响应的情况几乎不再出现。

3、总结建议

如果大家平时使用了Tomcat或者undertow作为web服务器,建议不要采用官方的默认配置。要结合实际业务场景和服务情况来设置,避免像楼主一样踩坑。

io-threads: IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接,默认设置每个CPU核心一个线程,不可设置过大,否则启动项目会报错:打开文件数过多。

worker-threads: 阻塞任务线程池,当执行类似servlet请求阻塞IO操作,undertow会从这个线程池中取得线程。它的值取决于系统线程执行任务的阻塞系数,默认值是 io-threads*8,这里我们手动指定相应的值。

常见配置

# Undertow 日志存放目录
server.undertow.accesslog.dir=
# 是否启动日志
server.undertow.accesslog.enabled=false 
# 日志格式
server.undertow.accesslog.pattern=common
# 日志文件名前缀
server.undertow.accesslog.prefix=access_log
# 日志文件名后缀
server.undertow.accesslog.suffix=log
# HTTP POST请求最大的大小
server.undertow.max-http-post-size=0 
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
server.undertow.io-threads=4
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
server.undertow.worker-threads=20
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
# 每块buffer的空间大小,越小的空间被利用越充分
server.undertow.buffer-size=1024
# 每个区分配的buffer数量 , 所以pool的大小是buffer-size * buffers-per-region
server.undertow.buffers-per-region=1024
# 是否分配的直接内存
server.undertow.direct-buffers=true

undertow官方源码可参考如下:

github.com/undertow-io…

ioThreads = Math.max(Runtime.getRuntime().availableProcessors(), 2);

workerThreads = ioThreads * 8;