背景:
线上服务部署在 K8s 环境中,近期遇到一个诡异问题:一旦集群因cpu过低触发缩容,缩容过程中有些Pod cpu立马跑满100%,直接命中k8s cpu Limit 限制,而其他pod cpu则正常。更诡异的是,进行手动扩容后,部分Pod的cpu占用率一直无法下降,长期处于满负荷状态。该服务使用的brpc 框架版本为1.15。
场景复现:服务缩容触发时,线上的pod数量为8个,cpu使用率为40%,当8个pod开始缩容到6个时,其中每次会有1个到2个pod的cpu会突然飙升至100%,但其他pod的cpu使用率维持正常。
结论:
先给结论,修改了brpc启动的工作线程。让工作线程数量为pod limt cpu的3倍,目前在进行扩缩容时,尚未出现同样问题。但也不能完全确定已经解决,待后续继续观察。
修改工作线程方法(注: 一定要同时修改):
启动参数
-bthread_concurrency=12
代码参数
brpc::ServerOptions options;
options.num_threads = 12;
排查:
从现象上看,感觉可能有两个方向需要排查。一个是aws云服务,承载pod的node节点是否稳定。另一个是brpc框架设置和业务代码是否存在问题。
第一感觉应该是自身代码的问题,所以先监控下cpu的相关情况。brpc有大量的内置监控,先从指标入手。翻阅官方文档:高效率排查server卡顿 | bRPC
发现brpc内部有4个指标需要特别关注的
bthread_worker_count: 工作线程的个数
bthread_worker_usage: 正在被使用的工作线程个数
system_core_count: cpu核心的个数
process_cpu_usage: 正在使用的cpu核数
到这其实算直接发现了一个问题,k8s容器上cpu limit是4,但bthread_worker_count监控出来却是32。查阅了一下源码,在默认情况下bthread_worker_count识别到的是物理机上的cpu数量。以下是自己的猜测,在程序缩容是,pod qps开始上涨,大量闲置brpc工作线程被激活,创建了大量的bthread,导致cpu飙升超出pod limit。
知识点:
bthread:用户态的"轻量级协程",数量可以非常多(百万级)。它本身不直接对应操作系统线程,而是由调度器分配执行。
bthread worker: 真正执行代码的底层线程,每个 worker 都是一个 pthread(操作系统线程)。
关系:
- bthread 运行在 worker 之上
每个 worker 线程内部运行一个调度循环,不断从全局队列或本地队列中取出一个 bthread 并执行它。
当 bthread 主动让出(如阻塞 I/O、等待锁、bthread_yield())或时间片用完,worker 会切换到下一个可运行的 bthread。
- M 个 bthread 映射到 N 个 worker
M(bthread 数量)可以远大于 N(worker 数量)。
每个 worker 在任意时刻只能执行一个 bthread,但可以通过快速切换来并发处理大量 bthread。
- worker 不绑定 bthread
bthread 在执行期间可能被不同的 worker 先后调度,没有固定关联。这保证了负载均衡和高效利用。