Apache Kafka 是不是适合用作无序任务队列?

Posted

技术标签:

【中文标题】Apache Kafka 是不是适合用作无序任务队列?【英文标题】:Is Apache Kafka appropriate for use as an unordered task queue?Apache Kafka 是否适合用作无序任务队列? 【发布时间】:2016-07-12 09:34:08 【问题描述】:

Kafka 根据生产者分配的分区将传入消息分成多个分区。来自分区的消息然后被不同消费者组中的消费者消费。

这种架构让我对使用 Kafka 作为工作/任务队列持谨慎态度,因为我必须在生产时指定分区,这间接限制了哪些消费者可以在上面工作,因为一个分区只发送给一个消费者消费群体。我宁愿不提前指定分区,以便可以执行该任务的任何消费者都可以这样做。有没有办法在 Kafka 架构中构建分区/生产者,让下一个可用的消费者拉取任务,而不必在生产工作时通过选择分区来提前拆分工作?

对该主题仅使用一个分区会将所有任务放在同一个队列中,但是每个消费者组的消费者数量限制为 1 个,因此每个消费者必须在不同的组中。然后所有的任务都被分配给每个消费者组,但这不是我正在寻找的那种工作队列。

Apache Kafka 是否适合用作任务队列?

【问题讨论】:

附带说明:您的问题可以使用具有共享主题消费者订阅的 Apache Pulsar 来解决。见pulsar.apache.org/docs/latest/getting-started/… 【参考方案1】:

我会说这取决于规模。您预计单位时间内有多少任务?

您所描述的最终目标基本上是 Kafka 在默认情况下的工作方式。 当您生成消息时,默认(最广泛使用)选项是使用随机分区器,它以循环方式选择分区,保持分区均匀使用(因此可以避免指定分区)。 分区的主要目的是并行处理消息,所以你应该以这种方式使用它。 分区用于的其他常用“事物”是确保某些消息以与它们产生的顺序相同的顺序被消耗(然后您指定分区键以使所有此类消息最终都在同一个分区中。例如使用@ 987654321@ 作为密钥将确保所有用户都以这种方式处理)。

【讨论】:

感谢您的回答 Marko,也许我们可以通过一个示例来深入了解这一点。假设我们有 20 个分区和 2 个工作人员,并且有 100 个新作业进来。使用轮询,作业消息被分发到每个分区 5,然后每个消费者得到 10 个分区,即 50 个作业。假设一个消费者的 50 个工作需要 100 毫秒(所有这些工作的总和),但另一个消费者的 50 个工作需要 2 分钟。提早结束的消费者能否帮助超负荷的消费者?卡夫卡是否对同等工作困难做出某种假设? 嘿,Marko,我认为我在该评论中的最后一个问题在这里触及了问题的核心,如果您可以为此添加更多细节,那么我一定会接受您的回答!跨度> 这 100 条消息中的任何一条都会进入一个随机分区,并会被这两个(即随机)消费者中的一个接收到,然后是第二条消息,然后是第三条,......所以它不是就像每个消费者都会收到大量的 50 条消息,即他们“互相帮助”。但是你为什么要将自己限制为只有 2 个消费者线程呢?此外,只有在处理完每条消息后,您才会提交偏移量,以确保在处理不成功时不会丢失任何消息。【参考方案2】:

将 Kafka 用于任务队列是个坏主意。 改用 RabbitMQ,它做得更好,更优雅。

虽然您可以将 Kafka 用于任务队列,但您会遇到一些问题: Kafka 不允许多个消费者使用单个分区(根据设计),因此,例如,如果单个分区充满了许多任务并且拥有该分区的消费者很忙,则该分区中的任务将“饿死”。 这也意味着主题中任务的消费顺序将与任务产生的顺序不同,如果需要按特定顺序消费任务,这可能会导致严重问题(在 Kafka 中要完全实现,您必须只有一个消费者和一个分区 - 意味着只有一个节点串行消费。如果你有多个消费者和多个分区,主题级别的任务消费顺序将无法保证。

事实上 - Kafka 主题不是计算机科学方式中的队列。队列意味着先进先出 - 这不是您在主题级别在 Kafka 中获得的。

另一个问题是很难动态改变分区的数量。添加或删除新工人应该是动态的。如果您想确保新工人将在 Kakfa 中获得任务,您必须将分区号设置为最大可能的工人。这还不够优雅。

所以底线 - 改用 RabbitMQ 或其他队列。

说了这么多——Samza(通过linkedin)正在使用kafka作为某种基于流的任务队列: Samza

编辑: 规模考虑:我忘了提到 Kakfa 是一个大数据/大规模工具。如果你的工作量很大,那么尽管我之前写了一些东西,但 Kafka 对你来说可能是一个不错的选择,因为处理大规模是非常具有挑战性的,而 Kafka 在这方面做得非常好。如果我们谈论的是较小的规模(例如,每秒最多几个/数百个作业),那么与 RabbitMQ 相比,Kafka 也是一个糟糕的选择。

【讨论】:

可能还值得一提的是,提交偏移量很快变得复杂,以处理需要重试的失败任务。 “在Kafka中完全实现你必须只有一个消费者和一个分区”是不正确的。根据分区键为主题中的每个分区保证顺序。因此,如果顺序很重要,您需要按顺序重要的值进行分区。这实际上比rabbitmq更强的订购保证,可能只有一个消费者来保证订购。 每个分区一个消费者,而不是每个主题。问题也在rabbitmq中。如果您希望以有保证的顺序处理消息,则该队列只能有一个使用者。您不能使用并行消费者按顺序处理工作。 Kafka 的主要优势在于大量数据的流式传输。如果您不流式传输大量数据 - Kafka 可能是一个糟糕的选择 当您以任何有意义的方式拥有多个消费者时,无法保证订单。如果一个消费者失败并且任务被重新排队怎么办?如果消费者 A 在消费者 B 之前完成任务,即使他们以相反的顺序收到任务怎么办?卡夫卡有铁定的订购保证。绝大多数消息队列都没有,包括rabbit mq,除非你有一个生产者和一个消费者。【参考方案3】:

尝试将 Kafka 用作消息队列有两个主要障碍:

    如Ofer's answer 中所述,您只能从单个消费者消费单个分区,并且仅在分区内保证处理顺序。因此,如果您不能在分区之间公平分配任务,这可能是个问题

    默认情况下,您只能确认处理到给定点(偏移量)之前的所有消息。与传统消息队列不同,您不能进行选择性确认,并且在失败的情况下,选择性重试。这可以通过使用kmq 来解决,它在附加主题的帮助下添加了个人确认功能(免责声明:我是 kmq 的作者)。

RabbitMQ 当然是一种替代方案,但它也提供了不同的(较低的)性能和复制保证。简而言之,RabbitMQ 文档声明代理 is not partition tolerant。另请参阅我们对消息队列与数据复制的比较,mqperf。

【讨论】:

【参考方案4】:

围绕工作或任务队列中任务的执行顺序展开了很多讨论。我会提出执行顺序不应该是工作队列的特征的概念。

工作队列是一种通过应用可控数量的工作线程来完成不同任务来控制资源使用的方法。对队列中的任务执行处理顺序意味着您也在对队列中的任务执行完成顺序,这实际上意味着队列中的任务将始终按顺序处理,下一个任务仅在前一个任务的 END 之后处理。这实际上意味着您有一个单线程任务队列。

如果执行顺序在您的某些任务中很重要,那么这些任务应在完成后将序列中的下一个任务添加到工作队列中。要么,要么您支持顺序作业类型,该类型在处理时实际上在一个工作人员上按顺序处理作业列表。

工作队列不应该实际安排它的任何工作 - 下一个可用的处理器应该总是处理下一个任务,而不考虑任务完成之前或之后发生的事情。

我也将 kafka 视为工作队列的基础,但我研究得越多,它看起来就越不像所需的平台。

我认为它主要用作同步不同资源的一种方式,而不是执行不同作业请求的一种方式。

我认为工作队列中另一个重要的领域是支持任务的优先级。例如,如果队列中有 20 个任务,并且有一个具有更高优先级的新任务到达,我希望该任务跳到行首以由下一个可用的工作人员接手。 Kafka 不允许这样做。

【讨论】:

【参考方案5】:

我正在开发一个在 kafka 之上实现作业队列的库,https://github.com/JingIsCoding/kafka-job-queue 我正在使用多个队列来维护准备好处理的任务、未来任务和死任务,欢迎贡献

【讨论】:

以上是关于Apache Kafka 是不是适合用作无序任务队列?的主要内容,如果未能解决你的问题,请参考以下文章

高盛弹性Apache Kafka集群实践

Kafka--kafka基础知识

初识Apache Kafka+JAVA程序实例

Apache kafka 工作原理介绍

初试kafka消息队列中间件一 (只适合初学者哈)

Kafka介绍