Google PubSub和来自TOPIC的重复消息

时间:2018-12-17 21:40:59

标签: java google-cloud-platform google-cloud-pubsub

如何防止Google Cloud PubSub中出现重复的味精?

说,我有一个代码可以处理所订阅的味精。

说,我有2个具有相同服务且具有此代码的节点。

一旦收到消息但尚未确认,另一节点将收到相同的消息。这就是我们有两个重复的消息的问题所在。

void messageReceiver(PubsubMessage pubsubMessage, AckReplyConsumer ackReply) {

        submitHandler.handle(toMessage(pubsubMessage))
                .doOnSuccess((response) -> {
                    log.info("Acknowledging the successfully processed message id: {}, response {}", pubsubMessage.getMessageId(), response);
                    ackReply.ack();  // <---- acknowledged
                })
                .doOnError((e) -> {
                    log.error("Not acknowledging due to an exception", e);
                    ackReply.nack();
                })
                .doOnTerminate(span::finish)
                .subscribe();
    }

对此有什么解决方案?这是正常行为吗?

3 个答案:

答案 0 :(得分:2)

Google Cloud Pub / Sub使用“至少一次”交付。来自the docs

  

通常,Cloud Pub / Sub按发布的顺序一次发送每条消息。但是,有时邮件可能会乱序发送或不止一次发送。通常,要容纳多于一次的传递,则要求您的订阅者在处理消息时必须idempotent

这意味着它保证将传递消息1:N次,因此,如果您不通过其他方式对其进行重复数据删除的方式进行管道传输,则有可能多次获得该消息。您无法定义任何设置来保证一次交货。该文档确实引用了您可以使用Cloud Dataflow的PubSubIO获得所需的行为,但是that solution appears to be deprecated

  

您可以使用Cloud Dataflow PubsubIO对Cloud Pub / Sub消息流进行一次处理。 PubsubIO对自定义消息标识符或由Cloud Pub / Sub分配的消息标识符重复删除消息。

说了这么多,我从未实际上见过Google Cloud Pub / Sub发送两次消息。您确定这确实是您遇到的问题,还是因为您未在“确认截止日期”内确认消息而重新发布了消息(如上所述,默认值为10秒)。如果您不同意,它将重新发出。来自the docs (重点是我)

  

为单个主题创建了一个订阅。它具有几个可以在创建时设置或以后更新的属性,包括:

     
      
  • acknowledgment deadline如果您的代码未在截止日期之前确认该消息,则会再次发送该消息。。默认值为10秒。您可以指定的最大自定义截止期限为600秒(10分钟)。
  •   

如果是这种情况,只需在截止日期之前确认您的消息,您就不会经常看到这些重复项。

答案 1 :(得分:0)

您可以使用Memorystore中的Redis来重复删除邮件。您的发布者应在将其发布到PubSub之前将跟踪iD添加到消息正文中。另一方面,客户端(订户)应检查跟踪ID是否在缓存中-跳过该消息。如果没有这样的消息,请处理该消息,并在7-8天的到期时间(PubSub截止日期为7天)中添加跟踪ID到缓存。您可以通过这种简单的方式授予收到的正确消息。

答案 2 :(得分:0)

给定主题中的所有消息均具有唯一的messageID字段:

  此消息的ID,由服务器在发布消息时分配。 保证在主题内具有唯一性。该值可由订阅者读取,该订阅者通过subscriptions.pull调用或推送传递接收PubsubMessage。发布者不得在topic.publish调用中填充它。

您可以使用它来重复删除传入的消息。无需手动分配ID。

在分布式系统中(例如,给定订阅的使用者的多个实例),它要难一些。您将需要一个全局同步机制,最简单的方法是设置数据库(例如Redis)并使用它来保留已处理的消息ID。

您应该查看Replaying and discarding messages,其中介绍了如何配置邮件保留。

订阅有两个属性:

  • retain_acked_messages-保留确认消息,
  • message_retention_duration-保留邮件多长时间。

如果您不打算将订阅倒带到过去的某个时间点,例如如果您不打算重新处理邮件或存在迫使您重置订阅的错误,则可以设置 retain_acked_messages=false message_retention_duration='3600s' 。这将使您仅保留最后一小时的消息ID。

请记住,PubSub消息也有publish_time,因此您无需将其添加到消息的数据中。可以与message_id一起使用。这两个都是由PubSub服务器在收到消息时设置的。