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

时间:2014-07-24 22:05:42

标签: architecture message-queue motion-detection

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

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

因此,关键要求似乎是:

  1. 一种不按顺序接收图像的网络服务,可以将它们分组为上一对和下一对,然后计算图像是否与之前的图像相比具有运动效果。
  2. 许多图像制作人 - 平均而言似乎每秒约有100张图像
  3. 许多动作检测消费者
  4. 优先考虑延迟超过吞吐量。
  5. 我在考虑使用单个邮件队列。生产者将图像文档推送到队列中。然后,运动检测工作人员从该队列中读取,并添加一个' diff_percentage'该文档的字段,并在数据库中更新该记录。

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

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

3 个答案:

答案 0 :(得分:3)

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

以下是在消费者端做什么:

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

您的消费者机器需要有足够的工作记忆来存储图像。如果平均在给定时间戳和时间戳之间或之后直接接收100个图像,并且图像平均大小为1MB,则图像将一起占用100 * 2 * 1MB = 200MB的内存。

创建第二个内存中队列,以跟踪尚无法比较的图像。如果在使用当前时间戳接收图像时哈希表中没有具有先前时间戳的图像,则工作者将具有其时间戳的图像指针放到该队列上。第二组工作程序从此队列中获取时间戳,并测试先前时间戳的图像是否同时可用。如果是这样,它会比较图像,否则会将图像和时间戳推回到队列中。

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

具有单个使用者的第三个队列负责更新数据库。第三个队列可能是也可能不是跨网络,就像第一个队列一样。在前两组中的工作者比较了两个连续图像之后,它将结果推送到第三个队列。此队列的使用者获取队列的内容并将它们同步到数据库。它可以通过一个事务为每几个(比如10个)比较做到这一点,以最小化延迟,或者每秒在单个事务中汇集所有内容,以最大化吞吐量。不要单独为每个图像比较创建一个事务,这可能比你想要的要慢得多。

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

当一个worker发出读取请求时,它将等待管理器的回复(而不是在它发出插入请求时)。它可能会传递一个回调和睡眠,或者输入一个螺旋锁检查"回复就绪"共享变量的值(取决于您的编程环境,这可能归结为引擎盖下的相同内容)。当然,你宁愿不让你的工作人员等待,但大多数等待时间非常短暂,这种方法肯定比全局锁定方法更快。

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

备注

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

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

答案 1 :(得分:2)

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

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

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

然后,并行运行尽可能多的消费者,可以唤醒,处理大量图像,并在文件用完时进入睡眠状态。他们需要通过让他们在不同的目录上工作而彼此独立工作。我希望他们为每个图像添加diff_percentage的目录,并在目录(timedate)完成时添加另一个文件。这使他们很容易重新启动并赶上,以防他们意外停止。

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

答案 2 :(得分:0)

  1. 我建议您不要将图像直接存储在数据库中。将所有二进制信息推入和推出数据库很容易造成瓶颈。将图像存储在blob商店(例如Amazon s3或Google Cloud Storage)上,并将对它们的引用存储在数据库中。
  2. 我建议您查看Apache Kafka作为邮件队列的选项。它支持许多并发读者从单个队列中读取所有消息。