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

73

Kafka根据生产者指定的分区将传入的消息进行分割。然后,不同消费者组中的消费者会消耗来自分区的消息。

这种架构使我对将Kafka用作工作/任务队列感到担忧,因为在生产时必须指定分区,这间接限制了哪些消费者可以处理它,因为一个分区只发送给消费者组中的一位消费者。我宁愿在事先不指定分区,以便任何可用于执行该任务的消费者都可以这样做。是否有一种方式在Kafka体系结构中对分区/生产者进行结构化,以便任务可以被下一个可用的消费者拉取,而无需事先通过选择分区来拆分工作?

仅使用此主题的一个分区会将所有任务放入相同的队列中,但是,每个消费者组仅限于1个消费者,因此每个消费者必须在不同的组中。然后,所有任务都分配给每个消费者组,这不是我要寻找的工作队列类型。

Apache Kafka适合用作任务队列吗?


顺便提一下:您的问题可以使用Apache Pulsar解决,它具有共享主题-消费者订阅功能。请参见http://pulsar.apache.org/docs/latest/getting-started/ConceptsAndArchitecture/#Subscriptionmodes-x0pyo - user2488286
5个回答

62

在任务队列方面使用Kafka是一个不好的想法。 相反,请使用RabbitMQ,它做得更好、更优雅。

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

事实上,Kafka主题不是计算机科学方式的队列。队列表示先进先出-这不是您在主题级别上获得的内容。

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

所以结论是-请使用RabbitMQ或其他队列。

话虽如此-Samza(由领英开发)正在使用Kafka作为一种基于流的任务队列: Samza

编辑: 规模考虑:我忘记提到Kakfa是一个大数据/大规模工具。如果您的作业速率非常高,则Kafka可能是一个不错的选择,尽管我之前写的问题,因为处理巨大规模的挑战非常困难,而Kafka非常擅长处理这个问题。如果我们谈论较小的规模(例如每秒几十/几百项作业),那么与RabbitMQ相比,再次选择Kafka是一个糟糕的选择。


5
值得一提的是,在需要重试失败任务时,提交偏移量很快就变得复杂起来。 - Ztyx
8
“在 Kafka 中,要完全实现这一点,您必须只有一个消费者和一个分区” 是不正确的。基于分区键,每个分区在主题中都有保证的顺序。因此,如果顺序很重要,则需要按照顺序重要的值进行分区。这实际上比 rabbitmq 更强的排序保证,rabbitmq 可能只有一个消费者来保证排序。 - Cody Gustafson
10
每个分区只能有一个消费者,而不是每个主题一个。这个问题也存在于RabbitMQ中。如果你想要确保消息按顺序处理,那么你只能为该队列设置一个消费者。你不能使用并行消费者按顺序处理工作。 - Cody Gustafson
2
Kafka的主要优势在于流式处理大量数据。如果您不需要处理大量数据,那么选择Kafka可能并不是一个好选择。 - Ofer Eliassaf
4
当有多个消费者时,无法以任何有意义的方式保证顺序。例如,如果一个消费者失败并重新排队,该任务会发生什么?如果消费者A在消费者B之前完成一个任务,即使它们按相反的顺序接收到任务,也会发生什么?Kafka具有坚如磐石的排序保证。绝大多数消息队列都没有这样的保证,包括Rabbit MQ,除非您只有一个生产者和一个消费者。 - ryeguy
显示剩余5条评论

10
在这个话题中,围绕着工作或任务队列中任务的执行顺序有很多讨论。我认为,在工作队列中,执行顺序不应该是一个特性。
工作队列是通过将可控制数量的工作线程应用于完成不同任务来控制资源使用的一种手段。在队列中对任务强制执行处理顺序意味着你也在强制执行队列中任务的完成顺序,这实际上意味着队列中的任务总是按照顺序依次处理,只有在前一个任务结束后才会处理下一个任务。这实际上意味着你拥有了一个单线程的任务队列。
如果任务执行顺序对某些任务很重要,那么这些任务应该在完成后将下一个任务添加到工作队列中。否则,你需要支持一个“顺序作业”类型,当处理时,它实际上会在一个工作线程上顺序处理一系列作业。
在任何情况下,工作队列都不应该对其工作进行排序 - 下一个可用的处理器应该始终获取下一个任务,而不考虑任务完成前后发生了什么。
我还在考虑以kafka为基础的工作队列,但是我越研究它,它看起来越不像所需平台。
我认为工作队列中另一个重要的领域是支持任务优先级。例如,如果我有20个任务在队列中,并且出现了一个具有更高优先级的新任务,我希望该任务跳到队列开头,由下一个可用的工作线程接手处理。但kafka不允许这样做。

你可以使用多个主题进行任务优先级划分,每个优先级对应一个主题。每个主题都被等分,并且每个等分都有一个协调进程。每个协调进程都有一个与主题对应的消费者,并根据主题的优先级维护一个本地优先级队列。然后,协调进程可以从这里开始向外扩散,将任务分配给它所控制的本地或远程资源池。 - undefined

9
我认为这取决于规模。您预计在一段时间内有多少个任务?
您所描述的最终目标基本上是Kafka默认的工作方式。当您生产消息时,默认(最广泛使用)选项是使用随机分区器,它以轮询的方式选择分区,保持分区的均匀使用(因此可以避免指定分区)。分区的主要目的是并行处理消息,因此应该以这种方式使用它。常用的另一个用途是确保某些消息按照生成顺序被消费(然后您以这种方式指定分区键,使所有这些消息都进入同一个分区。例如,使用userId作为键将确保以这种方式处理所有用户)。

2
感谢你的回答Marko,也许我们可以通过一个例子来深入了解这个问题。假设我们有20个分区和2个工作进程,并且有100个新的任务到来。使用轮询法,任务消息被分配到每个分区5个,然后每个消费者获取10个分区,即50个任务。假设一个消费者的50个任务需要100毫秒(总共),但另一个消费者的50个任务需要2分钟。完成得早的消费者能够帮助超载的消费者吗?Kafka是否对等任务难度做出某种假设? - morsecoder
1
嗨,马尔科,我认为我在那个评论中提出的最后一个问题触及了这里的核心问题,如果你可以再添加一些细节,那么我肯定会接受你的答案! - morsecoder
这100条消息中的任何一条都会进入随机分区,并被其中一个(即随机的)消费者接收,然后是第二条消息,然后是第三条,...所以不像每个消费者将获得50条消息的批量,即它们“互相帮助”。但是,为什么要限制自己只有2个消费者线程呢?此外,您只会在每条消息处理完成后提交偏移量,以确保如果处理失败,您不会丢失任何消息。 - Marko Bonaci

6
尝试将Kafka用作消息队列存在两个主要障碍:
1. 如Ofer在答案中所述,您只能从单个消费者消耗单个分区,并且仅在分区内保证处理顺序。因此,如果您无法在分区之间公平地分配任务,这可能是一个问题。
2. 默认情况下,您只能确认到达给定点(偏移量)的所有消息已被处理。与传统的消息队列不同,您无法进行选择性确认,在失败的情况下也无法进行选择性重试。使用kmq可以解决这个问题,它通过使用额外的主题添加了单独的确认功能(声明:我是kmq的作者)。
当然,RabbitMQ也是一种选择,但它还提供了不同(较低)的性能和复制保证。简而言之,RabbitMQ文档说明代理不具备分区容错性。还可以参阅我们关于具有数据复制的消息队列的比较:mqperf

0

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