Apache Kafka 是一个分布式数据存储,专门针对实时摄取和低延迟处理流数据进行了优化。它能够按顺序且增量地处理来自数千个数据源的持续数据流入。
为什么首先要使用 Kafka?
让我们首先来看看 Kafka 最初在 LinkedIn 萌发时所解决的问题。这个问题其实很简单:LinkedIn 当时接收到大量的日志数据,例如来自多个服务的日志消息、指标、事件以及其他监控/可观测性数据。他们希望以两种方式利用这些数据:
- 拥有一个在线的近实时系统,能够处理和分析这些数据。
- 拥有一个离线系统,能够在更长的时间范围内处理这些数据。
大部分处理工作都是为了分析,例如分析用户行为、用户如何使用 LinkedIn 等。
需求收集
问题很容易理解,但解决方案可能会显得相当复杂。这是因为问题本身就有很多的约束和需求。以下是此类系统需要的一些需求示例:
- 系统应该是 高度可扩展的。流行的产品每天可能产生数十或数百 TB 的事件、指标和日志数据。这就需要一个几乎可以线性扩展的分布式系统来处理如此高的吞吐量。
这很重要,因为我们需要支持极高的流量。很容易就能达到每秒数十万条消息。
- 它应该允许 “生产者”发送消息,而“消费者”订阅特定消息。这很重要,因为同一条消息可能有多个消费者(就像我们讨论的在线和离线系统),而且消息通常是异步的。
消费者还应该能够决定如何以及何时消费消息。例如,在我们讨论的问题中,我们可能希望一个消费者尽可能快地消费消息,而另一个消费者则每隔几小时消费一次。
- 消息可以是 不可变的(毕竟不需要删除日志数据),类似事务的语义和复杂的交付保证并不是重要的需求。
消息代理 vs Kafka
也许使用像 RabbitMQ 和 ActiveMQ 这样的消息代理可以解决上述问题,但实际上它们无法做到,让我们看看为什么:
- 消息批处理:由于我们要在消费者端拉取大量消息,因此一条一条地拉取消息是没有意义的。大多数时候,你会希望对消息进行批处理。否则,大部分时间都会浪费在网络调用上。
由于消息代理并不是真正用来支持如此高吞吐量的,它们通常不提供良好的消息批处理方法。
- 具有不同消费需求的不同消费者:我们讨论过拥有两种类型的消费者,一种是处理消息的实时在线系统,另一种是可能想要读取过去十二或二十四小时内接收到的消息的离线系统。
这种模式不适用于大多数消息代理或队列。这是因为某些消息代理(如 RabbitMQ)使用基于推送的模型,将消息从代理推送到消费者。这导致消费者的灵活性降低,因为消费者无法决定如何以及何时消费消息。
- 小而简单的消息:大多数消息代理中的消息大小通常较大。这不是一个 bug,而是设计使然。消息代理通常支持许多功能,例如不同的消息路由选项、消息保证、能够单独确认每条消息等,这会导致单个消息头很大。
只要消息量不大且不需要存储,大消息就没问题,但这恰恰是我们系统中想要做的。
- 分布式高吞吐量系统:最重要的需求之一是非常高的吞吐量。我们要支持每秒数十万条消息,甚至达到每秒数百万条。在单个节点上运行这个系统是不可行的。
我们需要一个能够支持这种吞吐量的分布式系统,而许多消息代理并不具备这一点。
- 大队列:消息代理对大队列大小的支持参差不齐。这取决于你使用的消息代理和你的配置,但互联网上充斥着许多人在消息代理队列大小方面遇到问题的帖子。
那么,让我们现在了解一下根据上述要求,Kafka 系统的架构应该是什么样的。
Kafka 的高层级设计架构
!imageApache Kafka 的高层级设计
上述设计的各个组件
- 主题:主题简单地就是一个消息流。生产者向主题发送消息,消费者从中轮询消息。
- 消费者:消费者仅仅是一个想要监听主题的应用程序。它持续轮询代理