fix(mq-starter-core): 终审两处停机缝隙——dispatcher 打烊拒收 + 定时器温和关闭
1) forceFlush 置 closed:driver.stop() 异步返回后残余线程再投递的消息一律 拒收抛 InProgress(不 ACK、下次启动重投),堵住正常停机丢最后一轮消息的缝隙; 检查先于去重,不留 processing 标记。 2) 定时器 shutdownNow→shutdown:不再打断正在执行的刷出,避免整批被误判失败; 后续执行照常取消,残留批由 forceFlush 接住(flushLock 串行兜底)。 终审 Important #1/#2,处置方案经用户确认。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -63,7 +63,7 @@ mq-spring-boot-starter 聚合 jar,业务方只引这一个(恒含 idemp
|
||||
|
||||
- **去重的两类跳过**决定 driver 能否 ACK:已 `success` → 正常返回让 driver ACK 挡掉重复;仍 `processing`(并发中或消费者崩溃残留标记)→ 抛 `MqMessageInProgressException` 让 driver **不要 ACK**、保留消息待 `processing` 标记过期后重投。搞错就是**静默丢消息**。
|
||||
- **单条消费失败**:先落库(**仅当配了 `errorHandler`**;`safeOnError` 对 null handler 静默跳过、不落库)+ `store.markFail()`(把去重标记从 processing 复位),再抛原异常交 driver 重投。跳过 `markFail` 会让标记卡在 processing,reclaim 重投时被去重挡掉并 ACK → 静默丢消息。
|
||||
- **批量消费**:满批 / 超时(`batchTimeoutMs` 缺省 60s,从本批首条算起;registry 全局单线程 `mq-batch-flush` 每 1s 扫)/ 停机(stop 时 `forceFlush`)三路径刷出,统一走 `doFlush`(`flushLock` 串行化,业务批量方法任一时刻至多一个调用);kill -9 仍丢内存未刷批(上界 = 一个超时窗口)。批量失败因消息已逐条 ACK 无法精确重投整批,故落库(同样只在配了 `errorHandler` 时)+ 吞掉不抛。
|
||||
- **批量消费**:满批 / 超时(`batchTimeoutMs` 缺省 60s,从本批首条算起;registry 全局单线程 `mq-batch-flush` 每 1s 扫)/ 停机(stop 时 `forceFlush`)三路径刷出(forceFlush 同时打烊:停机后迟到的消息拒收抛 InProgress、不 ACK、下次启动重投,堵住 driver.stop 异步返回的缝隙),统一走 `doFlush`(`flushLock` 串行化,业务批量方法任一时刻至多一个调用);kill -9 仍丢内存未刷批(上界 = 一个超时窗口)。批量失败因消息已逐条 ACK 无法精确重投整批,故落库(同样只在配了 `errorHandler` 时)+ 吞掉不抛。
|
||||
|
||||
driver 侧对 `MqMessageInProgressException` 的对应处理:`RedisStreamMqDriver.java` 捕获后不 `acknowledge()`(留 PEL 待 reclaim);`RocketMqDriver.java` 在通用 `catch(Exception)` **之前**单独捕获并返回 `RECONSUME_LATER`(绝不进耗尽落库分支——catch 顺序不能动,它是 `Exception` 子类)。边缘(**两端共性**,2026-07-14 复核):InProgress 白跑会烧重试计数——rocketmq 每次 `RECONSUME_LATER` 使 `reconsumeTimes` +1,烧光进原生 `%DLQ%`(进死信、非丢消息);redis-stream 每次 reclaim(xClaim)使 PEL delivery +1,`deliveryCount > maxRetry` 时**不再 dispatch** 直接判耗尽(配了 `errorHandler` 落库,没配则静默丢弃——业务可能一次都没重跑)。缓解同构:`mq.idempotent.processing-ttl` 小于 maxRetry 轮重投累计时长(rocketmq 默认 ~100s / redis-stream 为 `maxRetry × reclaim-min-idle` 默认 90s)。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user