Dubbo 线程池队列满触发拒绝策略,如何区分是服务端慢还是网络抖

解读

线程池打满后 Dubbo 直接抛出 RejectedExecutionException,调用方看到的现象都是“超时”或“线程池耗尽”。面试官想知道:

  1. 你能否把“服务端自身慢”与“网络 RTT 突然变大”这两种根因拆解开;
  2. 拆分过程是否可落地、可自动化、可灰度,适合国内常见的 4 层 LB + Kubernetes 集群环境;
  3. 结论能否指导下一步调优(扩容、线程池调参、熔断限流、网络包升级等)。

知识点

  1. Dubbo2.7.x 之后线程模型:Netty I/O 线程 → 业务线程池(fixed/cached/eager)→ 队列 → RejectHandler。
  2. 拒绝策略触发条件:队列长度达到 queues + maxThreads,与 I/O 线程无关。
  3. 服务端耗时 ≈ 业务线程池排队时间 + 实际 CPU/DB 处理时间;网络耗时 ≈ 客户端发送→服务端 I/O 线程收到 + 响应→客户端收到。
  4. 国内主流可观测体系:Prometheus + Grafana、CAT、SkyWalking、ARMS、SLS;4 层 SLB 多数只给 3xx/4xx/5xx,没有 RT 分位。
  5. 关键指标:
    • threadPoolQueueSize、threadPoolActiveCount(Dubbo 自带 MetricsFilter)
    • provider.successElapsed、provider.maxElapsed(埋点)
    • netty.ioRatio、netty.ioWaitRatio(自定义埋点)
    • TCP 重传、RTT、cwnd、丢包率(node_exporter + netstat/ss)
    • 客户端视角的 rt、timeout、retry 次数

答案

线上场景按“先止血、后定位”两步走,回答时分四段,每段给出可直接落地的命令或脚本,体现“可灰度、可回滚”。

第一段:30 秒止血

  1. 在 Provider 侧打开动态限流开关(Dubbo QoS 命令 setProviderLevel 0.8*maxThreads),或把预热权重调低,先让队列长度回落,避免持续拒绝。
  2. 同步在 Consumer 侧把失败率熔断阈值临时降到 50%,防止重试风暴。

第二段:1 分钟采集“能区分两者”的指标

  1. 打开 Dubbo 原生 MetricsFilter(spring-boot 场景加 dubbo.metrics.enabled=true),暴露 /actuator/metrics/dubbo.provider.thread.pool.queue.sizedubbo.provider.thread.pool.active
  2. 在 Provider 本地写一条日志 A:收到请求时打 recv_ts;业务方法返回后立即打 resp_ts
  3. 在 Consumer 侧日志 B:发送时 send_ts,收到响应 recv_resp_ts
  4. ss -tin 每 10 秒采样一次,抓取重传、rtt、cwnd。
  5. 把 1-4 的指标统一打到同一套 Prometheus,标签对齐:ip、pid、interface、method。

第三段:2 分钟计算差值

  1. 服务端耗时 S = resp_ts - recv_ts(日志 A 内计算,无网络)。
  2. 客户端耗时 C = P99(recv_resp_ts - send_ts)(日志 B 内计算,含网络)。
  3. 网络耗时 N ≈ C – S。
  4. 同时看队列长度曲线:
    • 若队列飙高且 S 同步飙高,N 平稳 → 根因是“服务端慢”;
    • 若队列飙高但 S 平稳,N 同步飙高,且 ss -tin 的 RTT、重传飙高 → 根因是“网络抖”。
  5. 用 Grafana 做模板:同一图表里画 S、N、queueSize,一眼可辨。

第四段:给出结论与后续动作

  • 服务端慢:拉下热点 SQL、Arthas trace 定位方法,或水平扩容 Pod。
  • 网络抖:检查交换机误码、K8s CNI 限流、SLB 后端健康探测频率,必要时把 Dubbo 协议换成 Triple/HTTP2 多路复用,或调大 buffer.size、开启 TCP_NODELAY
  • 无论哪种,都回滚第一段临时限流,并把线程池改为 eager 模式(core=max, queue=0),拒绝策略改抛自定义异常,方便下次 1 秒定位。

拓展思考

  1. 如果线程池队列长度正常却仍超时,要排查 I/O 线程是否被慢连接占满(Netty 默认 2*CPU 核),可在 Provider 加 ioThreadBusyRatio 埋点,>80% 即告警。
  2. 国内多活场景下,跨城专线偶尔 30 ms→200 ms 抖动,建议把“网络耗时 N”做成 SLA 指标写进网关,N>100 ms 自动切流。
  3. 对高并发秒杀,可提前用 Gatling/JMeter 压到 1.5 倍峰值,观察 S 与 N 的拐点,把“线程池 maxThreads + queue”与“网络 buffer”做成联动模板,上线前即可预测拒绝策略触发边界,避免真实现网再来“事后定位”。