消息需要访问共享数据时的消息队列体系结构

Dom*_*mra 7 architecture message-queue motion-detection

我必须建立一个运动检测服务.动态检测不对视频进行操作,而是对静止图像进行操作.

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

因此,关键要求似乎是:

  1. 不按顺序接收图像的Web服务能够将它们分组为上一对和下一对,然后计算图像是否具有与其先前图像相比的运动.
  2. 许多图像制作者 - 平均每秒大约有100张图像
  3. 许多运动检测消费者
  4. 优先考虑吞吐量的延迟.
  5. 不易单独使用的任务.

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

给定队列中的任务,工作人员可以单独操作该任务,方法是直接从数据库中取出图像,然后比较它,然后更新数据库中的记录.不幸的是,虽然这种方法运作得很好,但速度却非常慢.我想我需要减少数据库的读取.理想情况下,我希望这个"队列"能够缓冲,直到它具有给定作业所需的图像.类似于...当一个worker从队列中读取时,检查它需要比较的图像是否在队列中,如果没有,则转到数据库.

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

Jul*_*ian 4

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

以下是消费者端要做的事情:

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

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

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

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

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

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

当worker执行读取请求时,它将等待管理器的回复(而不是执行插入请求时)。它可能会传递回调并睡眠,或者输入自旋锁检查共享变量的“回复就绪”值(根据您的编程环境,这可能会归结为同一件事)。当然,您不希望您的工作人员根本等待,但大多数等待时间都会非常短暂,并且这种方法肯定会比全局锁定方法更快。

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

笔记

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

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