docs: 批量 idle flush 落地后口径同步(README/CLAUDE.md)
批量语义从"无 idle flush、低频停滞、重启丢批"改为"超时(缺省 60s)/停机 强制刷出,仅 kill -9 丢未满批";注解属性表补 batchTimeoutMs 行,TODO 勾选。 全景手册(docs/,gitignored)已同步修改,不在本 commit。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
13
README.md
13
README.md
@@ -140,7 +140,7 @@ public void onFile(FileData msg) { } // 只收 tag=node-01 的消息,
|
||||
@MqListener(topic = "LN_Topic", group = "g", idempotent = false)
|
||||
public void onData(FrontData msg) { }
|
||||
|
||||
// 批量:攒够 batchSize 条一次给你,参数要用 List
|
||||
// 批量:攒够 batchSize 条或超时 batchTimeoutMs(缺省 60s)一次给你,参数要用 List
|
||||
@MqListener(topic = "LN_Topic", group = "g", batchSize = 50)
|
||||
public void onBatch(List<FrontData> msgs) { }
|
||||
|
||||
@@ -164,7 +164,7 @@ public class MyErrorHandler implements MqConsumeErrorHandler {
|
||||
|
||||
> ⚠️ **不配 `errorHandler` = 失败静默丢弃**。未指定 `errorHandler` 时,单条 / 批量 / 重试耗尽的任何消费失败最终都会被 ACK、不落库、不留痕迹(ACK 终止是内建行为,与是否落库无关)。关键消费务必配上 `errorHandler` 落库,否则失败即丢、事后无从排查。
|
||||
>
|
||||
> ⚠️ **批量有几个坑**:参数必须写带具体元素类型的 `List<FrontData>`(裸 `List` 启动扫描直接抛 `IllegalStateException`);没有 idle flush,低流量攒不满会一直等,进程重启会丢内存里没攒满的那批;整批业务失败只回调 SINGLE 落库、不重投(所以批量务必配 `errorHandler`)。只适合高流量、能接受这些取舍的场景。批量方法同样可以加第二参 `MqContext`(拿到的是该批最后一条的元数据)。
|
||||
> ⚠️ **批量有几个坑**:参数必须写带具体元素类型的 `List<FrontData>`(裸 `List` 启动扫描直接抛 `IllegalStateException`);攒不满时最多等 `batchTimeoutMs`(缺省 60 秒,从本批第一条算起)会强制刷出,正常停机也会把没攒满的批刷出去,但 kill -9 / 断电仍会丢内存里那批(最多一个超时窗口内的量);整批业务失败只回调 SINGLE 落库、不重投(所以批量务必配 `errorHandler`)。批量方法同样可以加第二参 `MqContext`(拿到的是该批最后一条的元数据)。
|
||||
>
|
||||
> 另外 `batchSize` 与首参是否 `List` **错配时不报错**:启动时打一条 WARN 日志、仍按首参决定走不走批量(`batchSize=50` 配单个 payload 退化为逐条;`List` 首参配 `batchSize` 缺省按 1 条一批)。见启动日志 `[mq] @MqListener batchSize=...`。
|
||||
|
||||
@@ -179,7 +179,7 @@ public class MyErrorHandler implements MqConsumeErrorHandler {
|
||||
| 去重 `idempotent`(默认开启) | ✅(需 Redis) | ✅(需 Redis) |
|
||||
| 失败落库 `RETRY_EXHAUSTED`(需配 `errorHandler`,否则仅 ACK 丢弃) | ✅ | ✅ 落库+ACK |
|
||||
| 失败落库 `SINGLE` | ⚠️ 单条:落库+重投;批量:失败被吞(两端一致) | ⚠️ 单条:落库+重投;批量:失败被吞(两端一致) |
|
||||
| 批量 `batchSize` | ⚠️ core 内存攒批(driver 逐条喂);无 idle flush,低频停滞/重启丢未满批(两端一致) | ⚠️ core 内存攒批(driver 逐条喂);无 idle flush,低频停滞/重启丢未满批(两端一致) |
|
||||
| 批量 `batchSize` | ⚠️ core 内存攒批(driver 逐条喂);超时/停机强制刷出,仅 kill -9 丢未满批(两端一致) | ⚠️ core 内存攒批(driver 逐条喂);超时/停机强制刷出,仅 kill -9 丢未满批(两端一致) |
|
||||
| 重试 | ✅ PEL reclaim | ✅ 原生重投 |
|
||||
| 死信队列 | ❌ 只落库 | ⚠️ 原生 `%DLQ%` 通常被耗尽 ACK 旁路(InProgress 白跑烧光计数时仍会进) |
|
||||
| 顺序消息 | ❌ | ❌ 用的 Concurrently |
|
||||
@@ -262,6 +262,7 @@ core 是中立的,编排逻辑不依赖 Spring 和 Redis,可以直接单测
|
||||
| `group` | 必填 | 消费组名。同一组的多个实例**分摊**消费(一条消息只被组内一个实例消费),不同组**各收一份**全量。消费进度跟着组走:换个组名就是新组,两端都**从最新消息开始收、不回放历史**(rocketmq 新组默认最新位点,redis-stream 建组用 `$`) |
|
||||
| `concurrency` | 1 | 并发消费线程数,**当前两 driver 均未按此字段兑现**(redis 单线程 poll、rocketmq 用原生线程池默认值) |
|
||||
| `batchSize` | 1 | 攒批大小,大于 1 要用 `List` 参数;与首参是否 `List` 错配时启动打 WARN、仍按首参跑 |
|
||||
| `batchTimeoutMs` | 60000 | 批量攒批最长等待毫秒数(从本批第一条算起,到点未满也强制刷出);0/负数关闭;仅批量监听器生效 |
|
||||
| `maxRetry` | 3 | 最大重试次数(redis-stream 按 PEL delivery 计数判耗尽 / rocketmq 写入 broker `maxReconsumeTimes`;被去重标记挡回的 InProgress 白跑**两端都计入**) |
|
||||
| `tag` | `*` | 只收打了指定 tag 的消息(发送端三参 `send` 的第二参打 tag)。rocketmq 为 broker 服务端过滤、支持原生表达式(如 `A \|\| B`);redis-stream 当前不过滤、全收。详见「消费」一节的 tag 小节 |
|
||||
| `idempotent` | true | 是否去重(默认开启,需 Redis 可达;单个监听器关闭写 `idempotent = false`,全局关闭配 `mq.idempotent.enabled=false`) |
|
||||
@@ -306,8 +307,8 @@ RocketMQ 这边直接复用 rocketmq-spring 原生的 `rocketmq.*` 配置(`nam
|
||||
**对接异构发送方后,只有第一条消息能消费、后面成片丢失?**
|
||||
几乎必是去重 key 撞车:发送方没填或写死了 key(redis-stream 信封 `key` field / rocketmq 报文头 KEYS 属性),所有消息共用一把去重钥匙,第一条成功后其余全被判重复静默 ACK。让发送方保证 key 逐条唯一(见「Redis Stream 消息结构」一节),兑现前先在对应监听器上显式 `idempotent = false`。
|
||||
|
||||
**批量消费收不到 / 停滞 / 丢消息?**
|
||||
没有 idle flush,攒不满 `batchSize` 不触发;整批失败会被吞掉不重投。低流量把 `batchSize` 调小或改单条消费。
|
||||
**批量消费收不到 / 延迟 / 丢消息?**
|
||||
攒不满 `batchSize` 时最多等 `batchTimeoutMs`(缺省 60 秒)才刷出,嫌慢就把它调小;整批失败会被吞掉不重投(务必配 `errorHandler`);kill -9 / 断电会丢内存里没攒满的批,正常停机会强制刷出、不丢。
|
||||
|
||||
**消费失败后消息不见了、事后查不到?**
|
||||
多半是没配 `errorHandler`:没配时单条 / 批量 / 重试耗尽的失败一律只 ACK 丢弃、不留痕迹。关键消费用 `@MqListener(errorHandler = "beanName")` 落库,见「进阶用法」。
|
||||
@@ -326,7 +327,7 @@ P0,放量到生产前想补的:
|
||||
|
||||
- [x] 失败落库、去重、批量消费(core 已做)
|
||||
- [x] 给 rocketmq driver 补上去重和 `RETRY_EXHAUSTED` 落库,跟 redis-stream 对齐(去重 store 已抽成 driver 中立的 `mq-starter-idempotent-redis`)
|
||||
- [ ] 批量消费加 idle flush,现在低流量下攒不满会卡着,进程重启还会丢内存里没攒满的那批
|
||||
- [x] 批量消费加 idle flush(2026-07-14 落地:batchTimeoutMs 超时强刷 + 停机强刷;kill -9 仍丢内存未满批)
|
||||
- [ ] 顺序消息
|
||||
- [ ] 事务消息
|
||||
|
||||
|
||||
Reference in New Issue
Block a user