diff --git a/mq-starter-core/src/main/java/com/njcn/mq/container/MqConsumeDispatcher.java b/mq-starter-core/src/main/java/com/njcn/mq/container/MqConsumeDispatcher.java index cd6dcf3..877c63d 100644 --- a/mq-starter-core/src/main/java/com/njcn/mq/container/MqConsumeDispatcher.java +++ b/mq-starter-core/src/main/java/com/njcn/mq/container/MqConsumeDispatcher.java @@ -187,7 +187,9 @@ public class MqConsumeDispatcher { public static class Builder { private int batchSize = 1; private int batchTimeoutMs; // 缺省 0 = 关闭(注解缺省 60s 由 registry 传入) - private LongSupplier clock = System::currentTimeMillis; + // 单调时钟:只做相对差值(now - batchStartAtMs),用 nanoTime 免受 NTP 回拨影响 + // (墙钟回拨会让差值变负,批多躺回拨量,突破"延迟上界一个超时窗口"承诺)。 + private LongSupplier clock = () -> System.nanoTime() / 1_000_000; private boolean idempotent = false; private MqIdempotentStore store; private MqConsumeErrorHandler errorHandler; diff --git a/mq-starter-core/src/test/java/com/njcn/mq/container/MqConsumeDispatcherTest.java b/mq-starter-core/src/test/java/com/njcn/mq/container/MqConsumeDispatcherTest.java index a5ab0ea..09305c6 100644 --- a/mq-starter-core/src/test/java/com/njcn/mq/container/MqConsumeDispatcherTest.java +++ b/mq-starter-core/src/test/java/com/njcn/mq/container/MqConsumeDispatcherTest.java @@ -334,6 +334,22 @@ class MqConsumeDispatcherTest { assertFalse(d.needsTimeoutFlush(), "关闭超时的批量监听器不应纳入定时扫描"); } + @Test + void defaultClock_freshBatchNotDue() throws Exception { + // 不注入 clock,守护缺省单调时钟的纳秒→毫秒换算:错用裸 nanoTime 会让差值以纳秒计, + // 刚入缓冲的批立即被判超时刷出。 + List> consumed = new ArrayList<>(); + MqConsumeDispatcher d = MqConsumeDispatcher.builder() + .batchSize(3).batchTimeoutMs(60_000) + .consumer(consumed::add) + .build(); + + d.dispatch(MqMessage.of("k1", "", "a")); + d.flushIfTimeout(); + + assertEquals(0, consumed.size(), "刚入缓冲远未到 60s,缺省时钟不应判超时"); + } + @Test void forceFlush_flushesLeftovers() throws Exception { List> consumed = new ArrayList<>();