有没有办法为gcp中的发布/订阅标识重复的通知消息?

时间:2019-08-21 17:30:43

标签: google-cloud-platform

我是gcp的新手,不确定是否可以执行此操作,因此我需要一些建议。

我目前有一个系统,其中在某个文件夹中创建新对象时会收到有关主题的通知。之后,我有一个云功能,当在该主题中生成新通知时,将触发该功能。此功能从对象获取数据并更改云存储中的多个对象。

问题是我收到同一对象的多个通知,这将创建云功能的多个实例。这对于我的系统是不可行的。我不能在同一对象上运行多个云函数实例,因为它将更改其他对象并创建竞争条件。

我正在寻找一种仅获取一个对象的通知/确认接收到通知的主题的方法,因此请勿再次发送。另一种选择是使用云存储触发器直接从对象存储中触发,但问题是这将为存储桶中的所有对象而不是特定文件夹中的对象触发。

我愿意接受所有想法。

谢谢您的帮助。

2 个答案:

答案 0 :(得分:0)

如果您正在使用发布/订阅触发器,则对消息进行重复数据删除的责任在于您的应用程序,因为发布/订阅具有at-least-once behavior

  

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

但是,从同一文档中可以看到Dataflow是对消息进行重复数据删除的一种选择:

  

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

您可以创建一个数据流作业,以对您的Pub / Sub消息进行重复数据删除并直接触发您的Cloud Function(例如,通过HTTP,因为在另一个Pub / Sub主题上发送经过重复数据删除的消息将导致相同的至少一次行为) 。缺点是它增加了应用程序的开销(数据流作业+云功能)。

使用Cloud Storage触发器可能是一个主意;但是,从有关Event delivery mechanism的文档来看,Cloud Storage触发器是通过Pub / Sub传递的,这似乎在幕后,可能会导致相同的问题(我不知道是否在这种情况下进行了重复数据删除,文档尚不清楚)。如果您选择使用此功能,则如您所写,存储桶中每次对象更新时都会触发您的函数,因此您需要直接在Cloud Function的开头过滤事件,以检查是否有事要做(更新的对象是否有趣)。

根据我的经验,我建议您保留Pub / Sub触发器(因为上游应用程序已在某个文件夹中创建对象时已通知该事件)并处理Cloud Function中的重复项。在所有情况下,使Cloud Function幂等始终是一个好主意,重复消息也不是那么普遍(尽管我找不到任何数字)。要处理重复项,请在此Stackoverflow问题(@Kolban's comment)上检查Cloud Functions triggered by Cloud PubSub duplicate messages

  

我认为一种常见的技术是使用便宜的全局数据存储(redis / memcache)并保存每个已处理消息的message_id。在处理新消息之前,请检查您之前在缓存中没有看到它。

如果您确实不允许Cloud Function中的任何重复项,那么唯一的方法是使用HTTP触发器,唯一的触发器是确保at most once behavior

答案 1 :(得分:0)

当您必须在消息ID上指定pubsub消息时,@ norbjd的答案是正确的。

但是,我知道那不是你的情况。您想使用内容,更确切地说是here中提到的属性bucketIdobjectId。当这对仍在处理时,您不想在同一对象上开始新的过程。

如果我是对的,那么前面的答案将无济于事,除非在末尾提供有关redis / memorystore的提示(例如memcache,但不仅限于appengine)。实际上,您需要自己进行重复数据删除。您必须在某处存储一个信号令牌以说:“此对象正在处理”。在开始使用该功能时,请检查其是否处于活动状态。如果不是,请写出您的函数正在处理它。然后结束那里的功能,将其删除。

您可以使用多个组件来执行此操作。如果您有大量对象,请使用memorystore提高性能。如果您每天有合理数量的事件(少于30000),请选择Firestore /数据存储。 特别要提一下,如果您已经有一个云SQL实例,则也可以使用它(顺便说一句,您无需支付更多费用!)

最终提示。可能有两个函数同时启动,同时检查令牌并同时写入。为了防止这种情况,建议您除对象之外还编写消息ID,并按照以下过程进行操作:

  • 获取令牌
  • 检查是否存在,如果存在,则退出功能
  • 如果不写对象和messageid
  • 获取令牌。如果存在且该函数的当前messageid与messageid不同,则退出->并发写入,阻止
  • 如果messageid相同,请继续 ...
  • 过程结束,删除令牌。