复制代码

为懒人提供无限可能,生命不息,code不止

人类感性的情绪,让我们知难行难
我思故我在
日拱一卒,功不唐捐
  • 首页
  • 前端
  • 后台
  • 数据库
  • 运维
  • 资源下载
  • 实用工具
  • 接口文档工具
  • 登录
  • 注册

ERP

【原创】OpenTelemetry 在高并发场景下的线程阻塞问题:原理、诊断与终极解决方案

作者: whooyun发表于: 2025-07-07 10:17

一、问题现象:监控系统引发的服务瘫痪
系统升级至 Spring Boot 3 + Java 21 后,引入 OpenTelemetry 进行全链路监控。当 RabbitMQ 消费量突增至 1000+/秒时,出现严重故障:

HTTP 接口大面积超时:非消息处理接口响应延迟 > 30s

线程资源耗尽:Thread dump 显示 80% 线程处于 BLOCKED 状态

关键堆栈特征:

java
"otel-export-worker" #121 BLOCKED
  at okhttp3.internal.http2.Http2Writer.dataFrame
  locked okhttp3.internal.http2.Http2Writer@21a44196
  at okhttp3.internal.http2.Http2Connection.writeData
  
  
二、根本原因:HTTP/2 流控的蝴蝶效应
根本矛盾在于 OTLP 导出与业务请求共享 HTTP/2 连接资源,具体分三层解析:

1. 协议层:HTTP/2 流控的全局性
图表
代码
graph TB
    A[OTel 导出流] -->|耗尽连接窗口| B[共享HTTP/2连接]
    C[用户请求流1] --> B
    D[用户请求流2] --> B
所有流共享 连接级窗口(默认 64KB)

OTLP 数据积压导致窗口耗尽,阻塞所有流

2. 线程层:Spring Boot 的线程模型缺陷
properties
server.tomcat.max-threads=200 # 业务与监控共享线程池
OTel 导出占用 Tomcat 工作线程

阻塞线程无法释放导致线程池枯竭

3. 客户端层:OkHttp 的同步锁竞争
java
// OkHttp 源码片段
synchronized void writeData(...) { // 全局写入锁
  // 发送数据帧
}
一个慢速导出线程可阻塞所有需要网络写入的线程


解决方案:
1、监控指标同步上报修改为异步上报
2、使用同步上报则需要减少采样率(比如30%)
3、增加MQ消费者应用的数量,部署多节点
以上任何一个方案都可以解决当前碰到的问题,当然三个方案也可以一起用


本次排查使用到了arthas,使用到的命令有:
dashboard 查看到有线程占用cpu 100%以上

thread -b  查看被阻塞的线程

heapdump arthas-output/dump15.hprof    dump堆栈到指定文件