You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

启用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 15:45:32