【问题标题】:Azure queue - can I verify a message will be read only once?Azure 队列 - 我可以验证一条消息将只被读取一次吗?
【发布时间】:2012-11-16 02:15:34
【问题描述】:

我正在使用 Azure 队列,并且有几个不同的进程从队列中读取数据。
我的系统的构建方式假定每条消息只读取一次。
此Microsoft article 声称 Azure 队列具有至少一次传递保证,这可能意味着两个进程可以从队列中读取相同消息。
这个StackOverflow thread 声称,如果我使用 GetMessage 则消息对于所有其他进程变得不可见,因为不可见超时。

假设我使用 GetMessage() 并且在我 DeleteMessage 之前从未超过消息不可见时间,我可以假设我只会收到每条消息一次吗?

【问题讨论】:

  • 我想你可以假设。
  • 我向我的 Microsoft Azure 联系人提出了以下问题:“我严重依赖队列,并希望确保如果我 GetMessage() 然后 DeleteMessage() ,没有其他进程将获得来自队列的相同消息。根据Microsoft article,队列没有 At-Most-Once 保证。这是否意味着两个进程可以读取相同的队列消息并处理它?回应:是的——这正是它的意思。如果您希望每条消息都将在您必须使用 ServiceBus 队列时准确进行。

标签: azure azure-storage azure-storage-queues


【解决方案1】:

没有。可能会发生以下情况:

  • GetMessage()
  • 在数据库中添加一些记录...
  • 生成一些文件...
  • DeleteMessage() -> 意外失败(进程崩溃、实例重启、网络连接问题……)

在这种情况下,您的逻辑在没有调用 DeleteMessage 的情况下被执行。这意味着,一旦隐身超时到期,消息将出现在队列中并再次被处理。你需要确保你的进程是idempotent:

幂等性是数学中某些运算的性质,并且 计算机科学,它们可以多次应用而无需 在初始应用程序之外更改结果。

另一种解决方案是将服务总线队列与 ReceiveAndDelete 模式一起使用(请参阅 如何从队列接收消息 下的this 页面)。如果您收到该消息,它将被标记为已使用并且不再出现。这样您就可以确保它At-Most-Once 被交付(参见与存储队列here 的比较)。但是话又说回来,如果您在处理消息时发生了某些事情(即:服务器崩溃,...),您可能会丢失有价值的信息。

更新:

这将模拟存储队列中的 At-Most-Once。消息可以通过 GetMessage 多次到达,但只会由您的业务逻辑处理一次(存在您的某些业务逻辑永远不会执行的风险)。

  • GetMessage()
  • DeleteMessage()
  • AddRecordsToDatabase()
  • GenerateFiles()

【讨论】:

  • 假设我的进程永远不会崩溃并且 DeleteMessage 在不可见期间总是被调用,我可以假设没有其他进程会收到相同的消息吗?
  • 是(一些满足最小字符数的内容)
  • 如果是这种情况我可以这样做:queue.GetMessage();紧随其后的是 queue.DeleteMessage();在这种情况下,我有 At-Most-Once 保证。如果是这种情况,为什么 Microsoft 声称没有 At-Most-Once 保证?
  • 是的。即使 DeleteMessage 失败,您也不会执行任何业务逻辑,因此下次再次处理该消息是安全的。
  • 跟进 Sandrino 的示例:假设我的队列有 itemA,我有两个进程。可能两个进程都会执行 GetMessage() 并获取 ItemA,但其中一个(较慢的)在调用 DeleteMessage() 时会失败,因此不会执行业务逻辑。这是正确的吗?
【解决方案2】:

我认为队列消息中有一个名为DequeueCount 的属性,它是该消息出列的次数。它由队列服务维护。我认为您可以使用此属性来确定您的消息是否已被阅读过。

https://docs.microsoft.com/en-us/dotnet/api/azure.storage.queues.models.queuemessage.dequeuecount?view=azure-dotnet

【讨论】:

  • 如果 processA 检查 DequeueCount 并看到它为 0 会发生什么。然后 processA 失去 CPU 并且 processB 检查 DequeueCount 并发现它为 0。 ProcessB 然后将消息出列,然后 processA 再次唤醒并且也出队消息。这样,它们都在同一队列消息上执行业务逻辑。我在这里错过了什么吗?
  • @Gilad - DequeueCount 在项目出列时由服务设置,因此不存在线程问题。因此,您可以通过删除带有DequeueCount > 0 的任何消息来实现最多一次语义。但我认为没有任何方法可以通过队列获得一次性语义。
  • 正如“breischl”所说,DequeueCount 由队列服务维护。一旦消息出队,它将是+1。队列服务本身至少实现了一次,如果您检查了DequeuCount > 0,那么我认为您实现了最多一次。只有一件事,您最好将 dequeucount >0 的消息移动到另一个“毒消息”,而不是仅仅删除它们,因为这些消息可能意味着某些后台任务尚未完成。
  • DequeueCount 在并发场景中是否一致?两个线程是否可以同时出列同一消息并且都将其视为DequeueCount: 0?
  • 你不能依赖DequeCount。 @MikeAsdf 场景是有效的。在并发阅读场景中,Microsoft 不做任何保证。您需要使用服务总线
猜你喜欢
  • 2016-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-10
  • 1970-01-01
  • 2023-03-14
  • 2017-01-02
相关资源
最近更新 更多