消息代理与数据库和监控的区别

14
我的问题与这个问题有一些相似之处: 为什么我们需要像RabbitMQ这样的消息代理而不是像PostgreSQL这样的数据库? 在我的当前(半专业)项目中,我也到了决定是否选择数据库、基于消息代理(例如RabbitMQ)或完全不同的解决方案的时候。
假设有两个工具,工具A和工具B。当工具A运行并完成时,工具B可能需要做一些事情。执行工具A需要很长时间(> 60秒),而且通常工具B没有什么要做的。工具A为工具B提供一些元数据,以便工具B知道该做什么。
基于消息的解决方案:建立一个消息队列,工具B正在消费它。如果执行了工具A,并且应该运行工具B,则工具A会发布一条包含元数据的消息(包括元数据)到队列中,工具B接收到该消息后将使用该消息中的元数据进行运行。
数据库解决方案:每当工具A运行时,它都会添加一个数据库记录,其中包含时间戳、元数据和状态“RUNNING”。如果执行了工具A并且应该运行工具B,它将更新DB记录状态为“NEXT_TOOL_B”。工具B不断查询状态为“NEXT_TOOL_B”的记录。如果发现了有东西,工具B将使用数据库记录中的元数据进行运行。
虽然我知道数据库解决方案的缺点,例如来自工具B的连续轮询,但我在基于消息的解决方案中错过了一个功能:
每当第三个工具(例如控制面板UI)想要知道当前状态时,它也可以随时查询数据库,并且如果工具A仍在工作,则会在其中找到“RUNNING”状态。在消息解决方案中,我真的看不到一种“监视”状态的方法,除非完成消息将在队列上。

所以我的问题是,您是否能够想出一种方法来实现这一点,使用消息或任何其他不需要轮询的方法?


5
我不确定为什么你反对这两种工具。消息队列用于帮助编写松耦合应用程序(或应用程序组件),而数据库用于保存状态。如果您需要获取状态,请从数据库中获取。如果您需要使组件通信,请使用消息队列。如果因管理开销而不想使用两者,那么您必须做出选择并接受其中的缺点。因此,这是一个针对软件工程社区而非stackoverflow的问题。 - user8808265
有几篇文章,比如这篇,提倡使用消息队列而不是数据库进行工具间通信。当然,像锁定等数据库的缺点高度依赖于应用程序的规模。最后,作为一名控制工程师而非软件开发人员,我希望得到您的反馈,可能会选择混合解决方案,既不使用一个也不使用另一个,而是两者兼备。 - Clemens
1
想象一下,您想将工具A扩展到10个实例,工具B扩展到10个实例。如何确保来自工具A的任务仅被处理一次?所有10个实例都会轮询数据库,并且第一个发现准备好的任务必须锁定任务表并在持有锁的同时更新它...这不是很好。当某些工具A实例想要编写新任务时,等待并失败...您可能已经丢失了数据...而使用消息队列-您只需将所有这些10-100(无所谓)个工具B实例放置在一个工作队列上,只有一个实例将接收消息。 - Alex Buyny
3个回答

26

这个问题描述的情境是一个由多个不同部件组成的系统,它们一起工作以实现某种功能。在这种情况下,你有三个不同的进程{A,B,C},加上一个数据库和可选的消息队列。所有系统都接受一个或多个输入,执行某些过程,并产生一个或多个输出,这是它们存在的目的的一部分。在你的情况下,你希望得到的其中一个输出是系统及其处理的状态,这并不是一个完全不合理的要求。

队列还是数据库?

现在,到了你的问题。为什么使用消息队列而不是数据库?两者都是系统中相似的组件,因为它们都具有一定的存储能力。你可能会在制造冰箱的工厂里问同样的问题——何时使用生产线上的货架比使用仓库更合适?

数据库就像仓库——它们被设计用来存放许多不同的东西,并使它们都保持相对整齐。一个好的仓库可以让用户快速地找到仓库里的东西,并且避免丢失零件和材料。如果物品进去了,它可以很容易地再次取出,但不是立即的。

另一方面,消息队列就像组装线上工作站附近的货架。零件在那里积累,等待被运行该工位的人消耗。这些货架被设计用来容纳一小部分相同的物品——就像软件系统中的消息队列一样。它们靠近工作人员,因此当下一个零件准备好被加工时,可以很快地取出它(相比于去仓库需要花费几分钟或更长时间)。此外,工人可以立即看到货架上有什么——如果货架是空的,工人可能会休息一下,等待它再次积累一两个零件。

最后,如果工厂中某个部分过度生产(我们不喜欢这种情况,因为它表示浪费),那么货架就会被淹没,超额的产品需要放入仓库。信不信由你,在工厂里这种情况经常发生-有时站点会短暂停机,仓库就会作为一个长期的缓冲区。

何时使用哪种方法?

回到问题上来。当你预期产生的消息与消费的消息通常匹配,并且需要快速检索时,应使用消息队列。你不希望消息在队列中停留很长时间。软件队列系统,如RabbitMq,还执行一些非常特定的功能-例如确保工作只由一个处理器处理,并且如果第一个处理器停止,则可以由另一个处理器接手。

另一方面,对于需要在多个处理步骤之间保持状态的事物,应使用数据库。你的工作状态就是一个应该存储在数据库中的典型示例。继续使用工厂比喻-将其视为每个步骤完成时发送给生产计划员的报告。生产计划员将把它存储在数据库中。

在队列可能会变满或数据不能在一个作业步骤和另一个作业步骤之间丢失时,还需要使用数据库。例如,制造工厂通常会将其成品存放在仓库中,以待发运给客户。在你的应用程序中,对于所有长期(超过几秒钟)的存储需求,请使用数据库。

底线

大多数可伸缩的软件系统都需要队列和数据库,关键是知道何时使用每种方法。

希望这有些意义。


1
免责声明:我是集群任务服务的作者-CTS,这是一种提议的解决方案或其他相关工具使用模式。
事实上,从总体架构的角度来看,您所描述的功能似乎需要两种类型的解决方案:
  • 工具B作为工具A完成工作的结果而执行的部分是经典的事件驱动流程。通常情况下,消息在这方面更有效,但请继续阅读...
  • 您想要对流程进行一些状态观察/监控的部分绝对需要持久化状态(是的,甚至不在内存中,或者至少在分布式内存中,因为您以后会想要一个集群等)。
我认为基于数据库的队列在这里是一种解决方案。基于数据库的队列肯定比非基于数据库的方法具有较低的吞吐量。但是,它给您带来了一些好处,例如:
  • 保证持久性,除非发生真正的灾难,否则不会丢失任何任务/消息,即使队列是问题中最小的
  • 智能任务管理在数据库上同步,无需复杂的系统拓扑、主/从问题、故障转移等
  • 作为嵌入式解决方案获取这样的队列要容易得多——与单独的Redis/Rabbit/Kafka安装/服务相比,操作成本非常低

CTS 而言,它是一个集群感知的任务分发和管理系统,通过消费应用程序提供并运行嵌入式,您可以通过在 Tool A 的流程结束时将所有相关数据作为任务排队运行 Tool B 来解决问题。 同时,Tool C 可以使用 CTS 的 API 检查任务的状态并根据需要对其进行可视化。


0
您可以使队列的生产者和消费者更新 NoSQL 数据库或关系型数据库中的表。这将允许您随时查看请求的状态。它还可以让您利用推送消息而无需轮询的优势。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接