已采纳
回答正文
回复
讨论正文
该问题的正文、回答与讨论。
Kafka 3.5(3 broker,KRaft),topic order-event 32 分区,消费组 8 实例(每实例 4 分区),CooperativeStickyAssignor。
关键配置:max.poll.records=500,enable.auto.commit=false,max.poll.interval.ms=300000(5min),session.timeout.ms=10000。
消费逻辑:每条消息要调外部风控 API(P50 80ms,P99 2.3s,P999 偶发 8~15s),然后写 MySQL。为了吞吐,我用一个 16 线程的线程池并发处理同一批 500 条,用 CompletableFuture.allOf(futures).get(25, TimeUnit.SECONDS) 等待全部完成,再 commitSync。
get(25s) 抛 TimeoutException,但线程池里还有 3~5 个"僵尸任务"在跑(HTTP 连接还没断开)。max.poll.interval.ms 超时被踢组。max.poll.interval.ms,风险转移。CompletableFuture.cancel(true):对阻塞在 JDK HttpClient 的线程无效(HttpClient 不响应 Thread.interrupt())。CONNECT_TIMEOUT=5s、READ_TIMEOUT=10s:兜住了 P99,但 P999 仍有 15s+ 的 case(下游 GC 停顿导致)。SynchronousQueue + CallerRunsPolicy:高吞吐时主线程被阻塞,poll 间隔拉长,问题没消失只是换了个位置。CountDownLatch + 超时后走"标记放弃 + 补偿表"路径?还是用 invokeAll(tasks, timeout) 的语义?cancel(true) 更可靠的手段(比如用 ExecutorService.invokeAll + 超时后 shutdownNow 一个子池)?引用依据: