启用Exactly once delivery后Pub/Sub确认期限失效问题咨询
问题
已启用Exactly once delivery,且将订阅的Acknowledgement deadline设置为600s,但消息接收方仍会在60s或更短时间内重复收到同一消息。
测试代码如下:
final Subscriber subscriber = pubSubTemplate.getPubSubSubscriberTemplate(). subscribe("projects/xxxx/subscriptions/xxx-topic-sub", m -> { final String s = m.getPubsubMessage().getData().toStringUtf8(); logger.info("sub1 = " + Thread.currentThread().getName() + " = " + s + " " + m.getPubsubMessage().getMessageId()); try { logger.info("Processing in progress - going to sleep 5 minutes..."); Thread.sleep(1000 * 60 * 5); logger.info("Sleeping over"); } catch (InterruptedException e) { e.printStackTrace(); } }); subscriber.awaitRunning(); for (; ; ) { Thread.sleep(Long.MAX_VALUE); }
消息接收输出(注意时间戳):
08:19:06.277 INFO 26172 --- [sub-subscriber1] GcpTest : sub1 = global-gcp-pubsub-subscriber1 = HELLO 5185509958571237 08:19:06.278 INFO 26172 --- [sub-subscriber1] GcpTest : Processing in progress - going to sleep 5 minutes... 08:20:27.810 INFO 26172 --- [sub-subscriber2] GcpTest : sub1 = global-gcp-pubsub-subscriber2 = HELLO 5185509958571237 08:20:27.810 INFO 26172 --- [sub-subscriber2] GcpTest : Processing in progress - going to sleep 5 minutes... 08:21:57.854 INFO 26172 --- [sub-subscriber3] GcpTest : sub1 = global-gcp-pubsub-subscriber3 = HELLO 5185509958571237 08:21:57.855 INFO 26172 --- [sub-subscriber3] GcpTest : Processing in progress - going to sleep 5 minutes...
使用的依赖版本:
implementation("com.google.cloud:spring-cloud-gcp-starter-pubsub:3.3.0")
请问该问题是什么原因导致的?另外也不理解maxAckExtensionPeriod这类设置,相关文档未覆盖该内容。
原因分析
1. 自动ACK扩展的默认限制
Spring Cloud GCP Pub/Sub的订阅器默认会自动尝试延长消息的ACK截止时间,但默认的maxAckExtensionPeriod值为60秒。这意味着:
- 即便你在订阅端设置了600秒的ACK截止时间,客户端最多只会将ACK截止时间延长60秒,之后就会停止扩展
- 当消息处理时间(你这里是5分钟)超过了「初始ACK截止时间 + maxAckExtensionPeriod」的总和,Pub/Sub会认为消息未被正确处理,将其重新投递
2. 自动ACK的时机
你的代码中没有手动调用ACK/NACK方法,默认逻辑是当回调方法执行完成后才会自动ACK消息。但在回调sleep的5分钟内,客户端因maxAckExtensionPeriod限制无法继续延长截止时间,导致消息在60秒左右被重新推送。
关于maxAckExtensionPeriod的说明
这个参数控制客户端允许为单条消息延长ACK截止时间的总时长上限。举个例子:
- 如果订阅的初始ACK截止时间是10秒,
maxAckExtensionPeriod设为300秒(5分钟),客户端最多能把该消息的ACK截止时间延长到10+300=310秒 - 超过这个上限后,客户端不再尝试扩展,消息会被Pub/Sub重新投递到订阅端
解决方法
你需要在创建订阅器时,显式配置maxAckExtensionPeriod为足够覆盖消息处理时间的值,同时确保订阅的ACK截止时间设置合理:
Subscriber subscriber = pubSubTemplate.getPubSubSubscriberTemplate() .subscribe("projects/xxxx/subscriptions/xxx-topic-sub", m -> { // 你的消息处理逻辑 final String s = m.getPubsubMessage().getData().toStringUtf8(); logger.info("sub1 = " + Thread.currentThread().getName() + " = " + s + " " + m.getPubsubMessage().getMessageId()); try { logger.info("Processing in progress - going to sleep 5 minutes..."); Thread.sleep(1000 * 60 * 5); logger.info("Sleeping over"); } catch (InterruptedException e) { e.printStackTrace(); } }, // 配置订阅器参数 subscriberFactory -> { subscriberFactory.setMaxAckExtensionPeriod(Duration.ofMinutes(5)); // 同步订阅端的初始ACK截止时间设置 subscriberFactory.setAckDeadline(Duration.ofSeconds(600)); }); subscriber.awaitRunning();
内容的提问来源于stack exchange,提问作者Vity
相关产品推荐
相关产品推荐

