已采纳
回答正文
回复
讨论正文
该问题的正文、回答与讨论。
我负责一个 B 端实时运营大盘(Spring Boot 3.1 + JDK 17,Tomcat 线程池 400,单实例 8C16G),核心接口 /dashboard/overview 一次请求要并行聚合 7 个下游,返回一个统一 VO:
| 下游 | 类型 | P50 | P99 | 关键性 | |---|---|---|---|---| | 用户中心 | RPC | 15ms | 45ms | 必须 | | 订单服务 | RPC | 30ms | 80ms | 必须 | | 库存服务 | RPC | 25ms | 70ms | 必须 | | 营销中台 | RPC | 210ms | 850ms | 可降级(缺失时展示"暂无活动") | | 优惠券 | RPC | 18ms | 55ms | 可降级 | | 搜索热度 | DB | 40ms | 120ms | 可降级 | | 风控分 | RPC | 22ms | 60ms | 必须 |
List<CompletableFuture<Fragment>> futs = downstreams.stream()
.map(d -> CompletableFuture.supplyAsync(() -> d.fetch())) // 默认 commonPool
.toList();
CompletableFuture.allOf(futs.toArray(new CompletableFuture[0]))
.orTimeout(1200, TimeUnit.MILLISECONDS)
.join();
TimeoutException,整个大盘白屏,而不是"6 快 1 慢"的降级结果——orTimeout 只挡了 join,没停任务、也没做按片降级。orTimeout(1200) 改成 orTimeout(600):P99 降到 820ms,但超时率从 0.3% 涨到 4.2%,前端白屏更频繁;orTimeout 套在单个 future 上):P99 降到 650ms,达标了,但 join() 之后那个 500ms 超时的任务还在 commonPool 里跑,JFR 里能看到 commonPool 的 queue 在慢时段堆积,怀疑任务没被真正取消;社区里 id=1377(超时预算切分)、id=1501(N 条 DB 查询的预算分配)、id=922(orTimeout 不杀任务)我都读过了,但它们要么聚焦"预算怎么切"、要么聚焦"commonPool 耗尽",没正面回答"1 慢拖 6 快"的聚合层降级设计。想请教三个具体问题:
join() 之后把"超时的那一个"的任务真正停掉(JDK 17 没有虚拟线程的 Thread.stop,commonPool 任务没法 cancel 运行中线程)?有没有生产验证过的"超时即停"姿势(比如把任务跑在独立线程池 + 队列拒绝策略,或 Resilience4j 的 TimeLimiter + 自定义 executor)?