【问题标题】:Message queue architecture when messages need to access shared data消息需要访问共享数据时的消息队列架构
【发布时间】:2014-09-16 15:56:25
【问题描述】:

我必须建立一个运动检测服务。运动检测不适用于视频,而仅适用于静止图像。

此微服务需要能够接收乱序图像(带有时间戳)并确定图像是否与之前拍摄的图像不同(带有早于它的时间戳)。需要有多个运动检测工作人员。

因此,关键要求似乎是:

  1. Web 服务可以无序地接收图像,能够将它们分组为前一对和下一对,然后计算图像与其前一个图像相比是否具有运动。
  2. 许多图像制作者 - 整个过程似乎平均每秒大约 100 张图像
  3. 许多运动检测消费者
  4. 延迟优先于吞吐量。
  5. 不容易独立消耗的任务。

我正在考虑使用单个消息队列。生产者将图像文档推送到队列中。然后,运动检测工作人员从该队列中读取数据,并将“diff_percentage”字段添加到该文档,并更新数据库中的该记录。

给定队列中的任务,工作人员可以单独操作该任务,方法是直接从数据库中获取它之前的图像,并比较它,然后更新数据库中的记录。不幸的是,虽然这样可以很好地工作,但速度会非常慢。我想我需要减少从数据库中读取。理想情况下,我希望这个“队列”能够缓冲,直到它拥有给定工作所需的图像。比如......当一个工作人员从队列中读取数据时,检查它需要比较的图像是否在队列中,如果没有,则转到数据库。

谁能指出我正确的方向?也许队列不是我想要的?也许是一个队列,或者某种缓存桶?

【问题讨论】:

  • 如前所述,这个问题对于有用的答案来说似乎有点过于宽泛。这是网络服务还是本地系统服务? “许多”是多少(100、100K、100M)?您想优先考虑吞吐量还是延迟?降低计算成本或存储成本?这些都是可能决定解决问题的可行方法和不可行方法的所有因素。
  • 公平点 MooseBoys。我会尝试添加更多细节
  • 我不清楚问题是什么。您当前的方法是否太慢了?
  • 如果您想确信my answer 来自可靠或官方来源:我的硕士论文是关于网络通信的,我对数据流编程非常感兴趣。

标签: architecture message-queue motion-detection


【解决方案1】:

具有多个生产者和多个消费者的图像队列在这里似乎是正确的方法。对于这个答案的其余部分,我将抽象出这个队列的细节,因为这些取决于生产者和消费者的位置(物理上在哪台机器上)。

以下是消费者端的操作:

将图像暂时在内存中保存在hash table 中。键是时间戳,值是指向图像内容的指针(以及您可能想要保留的任何元数据)。一旦图像与连续时间戳的图像进行比较,就可以从哈希表中删除图像。

您的消费机器需要有足够的工作内存来存储图像。如果在给定的时间戳和它之前或之后的时间戳之间平均接收到 100 张图片,并且一张图片的平均大小为 1MB,那么这些图片将总共占用 100 * 2 * 1MB = 200MB 的内存。

创建第二个内存队列以跟踪尚无法比较的图像。如果在接收具有当前时间戳的图像时,具有先前时间戳的图像在哈希表中不可用,则工作人员将带有其时间戳的图像指针放入该队列。第二组工作人员从这个队列中获取时间戳,并测试前一个时间戳的图像是否同时可用。如果是,则比较图像,否则将图像和时间戳推回队列。

第一组和第二组worker的相对大小应该与图像在其直接后继之前到达的相对频率成正比。换句话说,如果 60% 的时间图像在其直接后继者之前进入哈希表(因此 40% 的图像在其直接后继者之后到达),则 60% 的工作人员应该在第一个集合中,而 40%应该在第二组。或者,您可以根据需求动态地将工人分配到一组;如果乱序行为往往波动很大,例如取决于一天中的时间,这可能是合适的。

具有单个消费者的第三个队列负责更新数据库。这第三个队列可能会或可能不会跨网络,就像第一个队列一样。在前两组的工作人员比较了两个连续的图像之后,它将结果推送到第三个队列中。该队列的消费者获取队列的内容并将它们同步到数据库。它可以在每几次(比如 10 次)比较中使用一个事务来执行此操作,以最大限度地减少延迟,或者将所有内容集中在每秒一个事务中,以最大限度地提高吞吐量。不要为每个图像比较单独创建事务,这可能会比您想要的慢得多。

图像比较工作者都需要读取和更新哈希表,因此您需要一种机制来防止竞争条件。在这种情况下,锁定机制是不合适的,因为它可能会成为应用程序的瓶颈。相反,只需要一个工作人员来管理哈希表,并让所有比较工作人员通过读取/插入队列向哈希表管理器发送请求。由于管理器的工作相对较轻(存储、检索和删除图像指针),它应该能够在大多数时间保持领先于读取/插入队列。

当一个worker做一个读请求时,它会等待manager的回复(而不是当它做一个insert请求时)。它可能会传递回调和睡眠,或者输入自旋锁检查共享变量的“回复就绪”值(取决于您的编程环境,这可能归结为同一件事)。当然,您宁愿根本不让您的工作人员等待,但大多数等待会非常短暂,而且这种方法肯定会比全局锁定方法更快。

第一次从哈希表中成功检索到图像后,管理员可以从表中删除该图像(因为该图像只会被请求与后续图像进行比较)。管理器应该从哈希表中删除指针,而不是删除图像本身。您可以使用reference counting 来确定何时应从内存中完全清除图像。虽然引用计数需要锁定或原子化,但这不会成为瓶颈,因为在任何给定时间最多有两个工作人员将访问图像,而大多数情况下不会直接影响引用计数。

备注

在上述设计中,我没有讨论图像何时进入永久数据库。这很可能发生在生产者端,在图像进入第一个队列之前。或者,我讨论过的第三个队列中的数据库同步代理可能会这样做。你不想让你的比较工作者或你的哈希表管理员承担这个责任。

如果您认为我的回答很有希望,我愿意提供额外的文档,例如(极简主义)流程图、工作人员的伪代码算法或粗略的数据流流量配置文件。

【讨论】:

  • 谢谢朱利安。明天我会仔细阅读。这似乎是一个很好的解决方案。
  • 不客气。仅供参考,我对从哈希表中删除图像指针的段落做了一些简化:图像只会被请求与连续图像进行比较,因此管理员在删除指针之前不需要计数。仍需与其前任比较的图像保留在辅助队列中。
  • @DominicBou-Samra:我开始认为我可能误解了“乱序”。您的意思是“来自不同生产者的序列是混合的”还是“新的时间戳可能出现在旧的时间戳之前”?在前一种情况下,我的答案可以通过省略第二组比较工人来简化,尽管我仍然建议使用带有管理器的内存哈希表。
  • 是的,我的意思是快照可以按任何顺序出现。时间戳总是正确的(即时间戳字段为 7:30 的快照,是在时间戳字段为 7:31 的快照之前拍摄的,即使它们是由生产者无序发送的)。
  • 在这种情况下,我坚持我目前的答案。
【解决方案2】:

我认为您的问题来自尝试为许多图像制作者使用单个队列。这会导致图像集中在一起,然后需要解开图像序列。

我的方法是将图像流式传输到带时间戳的目录和文件中。不要混合图像制作者,将它们分开。然后很容易扫描文件并用diff_percentage标记它们。

例如,Image Producer #1 将文件存储在目录 /IP1/date/time/sequence 中,其中 date 类似于 20140727 (2014-07-27),time 类似于 1542(下午 3:42),sequence 是计数器从 1 到 6000(每秒最多 100 帧,每分钟 60 秒)。为其他图像生产者复制此结构。这样,图像独立于工作人员进行存储,并且没有队列或数据库的瓶颈。

然后,并行运行任意数量的消费者,它们可以唤醒、处理一大块图像并在它们用完要处理的文件时进入睡眠状态。他们需要通过让他们在不同的目录上工作来相互独立地工作。我会让他们将文件添加到每个图像的 diff_percentage 目录中,并在目录(timedate)完成时添加另一个文件。这使他们可以轻松地重新启动并赶上,以防意外停止。

这是旧的“divide and conquer”方法。通过保持图像流分离,更容易在消费者进程之间划分工作。

【讨论】:

  • 使用文件系统可能不会比访问数据库快。
  • 谢谢布伦特。明天会仔细看看。似乎也是一个很好的解决方案。
  • @Julian,数据库是一个文件系统,上面有一个数据管理层。为什么你认为一个简单的文件系统并没有更快?
  • 好吧,为什么你认为它更快?两者都依赖于存储设备的速度,并且两者在顶部都有一个管理层,但 RDBMS 的优势在于它们专门针对存储和检索必须快速的情况进行了优化。另一方面,普通的旧文件系统通常仅在必须持久化或确实没有其他解决方案时使用。
  • 这个关于数据库与文件系统的讨论已经被打死了:dba.stackexchange.com/q/23124/24357programmers.stackexchange.com/q/150669/144312
【解决方案3】:
  1. 我建议您不要将图像直接存储在数据库中。将所有二进制信息推入和推出数据库很容易导致瓶颈。将图像存储在 blob 存储(例如 Amazon s3 或 Google Cloud Storage)上,并将对它们的引用存储在数据库中。
  2. 我建议您将 Apache Kafka 作为您的消息传递队列的一个选项。它支持多个并发阅读器从单个队列中读取所有消息。

【讨论】:

  • 是的。它们在 s3 中。我的意思是我们必须去分贝获取元数据
猜你喜欢
  • 2020-03-07
  • 1970-01-01
  • 1970-01-01
  • 2019-09-18
  • 1970-01-01
  • 2013-12-16
  • 1970-01-01
  • 1970-01-01
  • 2014-01-18
相关资源
最近更新 更多