几天前,我不得不设计一个基于高容量写入的扇出架构。
对于刚接触这一学派的人,我会非常简单地解释一下。基于写入的扇出架构尝试在写入期间使用所有业务逻辑。这个想法是为每个用户和每个用例准备好视图,这样当有人想要读取数据时,他们不必应用复杂的逻辑。然后阅读变得简单,而且大多数情况下您可以保证恒定的阅读时间。 Twitter 的架构是一个基于写入的扇出系统。
在不深入了解要求的细节的情况下,这里是 60k 英尺的概述。
-
大量写入
-
需要几乎恒定的读取时间
-
必须在商品硬件上具有容错性和可扩展性
-
还需要自由文本搜索和社交图遍历。
-
实时分析
我们设计的架构涉及三个数据库。用于存储传入数据的 MongoDB。 Redis 用于存储为该用户设计的每个用户的数据集。 ElasticSearch 用于存储需要自由文本或部分文本搜索的文本。
对于每个传入的数据集,都有业务逻辑决定将哪些数据集填充到 Redis 中(基于社交图连接)以及将哪些数据集提取并存储在 ElasticSearch 中以进行自由文本搜索。
听起来很简单!
鉴于此,我决定使用 Apache Kafka 作为其速度和可靠性的消息代理,然后使用 Storm 处理数据并实现基于写入的扇出架构。
细节决定成败。这就是我打算在这里分享的内容。在使用 Kafka 和 Storm 之前,您应该先了解一些事情。
Kafka - 消息队列
Kafka 是一个优雅的消息队列。您可以像发布订阅或广播一样使用它。它是怎么做到的?
这是解释相同内容的官方文档:
“传统上,消息传递有两种模型: 排队 和 发布-订阅 。在队列中,一群消费者可以从服务器读取每条消息,每条消息都发送到其中一个;在发布-订阅中,消息被广播给所有消费者。Kafka 提供了一个概括了这两者的单一消费者抽象—— 消费者群体 。
消费者使用消费者组名称标记自己,发布到主题的每条消息都会传递到每个订阅消费者组中的一个消费者实例。消费者实例可以在不同的进程中或在不同的机器上。
如果所有消费者实例都有相同的消费者组,那么这就像传统的队列平衡消费者负载一样。
如果所有消费者实例都有不同的消费者组,那么这就像发布-订阅一样工作,所有消息都会广播给所有消费者。”
Kafka 显着特征的快速总结
-
消息进一步划分为分区。
-
仅在分区内保证消息顺序。
-
生产者可以决定将数据发送到哪个分区。
有了这么多信息,根据分类创建主题是合乎逻辑的。对于每一种新类型的数据,我们都创建了一个新主题。例如,如果我们使用 Twitter,我们可以创建一个名为“tweets”的主题。我们会将所有推文创建数据推送到该主题中。但是关注用户是一个完全不同的用例。并且基于分类理论,我们将为此创建一个新主题,称为“关注”。所有与关注用户操作相关的数据都将发送到这个新主题“关注”。
现在让我们看看排序。仅在主题的分区内保证排序,每个主题可以有多个分区。消息只能发送到主题中的一个分区。
鉴于此,我们如何实现一致的排序。例如,让我们再次以 Twitter 为例。如果您有 10 条推文,您希望按相同的时间顺序查看它们。
所以现在给出了两个选择。每个主题只有一个分区并且有很多主题。例如,为每个用户设置一个主题。只有一个分区 这样,您始终可以保持消息传入的顺序。但这将意味着数亿个主题(每个用户一个主题)。
另一种选择是,为每个用户设置一个主题和一个分区。这样也可以保证订单。这意味着一个主题和数亿个分区。
现在我们了解到,这两种方法都不是最优的。过多的主题或过多的分区会导致性能问题。如果您阅读架构,很明显,这两种方法都会产生降低性能的开销。我不会深入探讨为什么会发生这种情况,但会告诉您我们是如何解决它的。
每个生产者都可以决定要将数据发送到主题中的哪个分区。这让我们可以选择拥有固定数量的分区,并将用户平均分配到这些分区中。我们发现,对于普通商品硬件和 3 节点集群,15k 分区是最佳选择。这是经过大量性能测试和优化之后的结果。因此,我们将用户输入内容平均分配到 15k 个分区中。我们没有为每个用户创建一个分区,而是为一组固定的用户使用一个分区。这使我们能够确保在没有数百万个分区的情况下为用户订购。
Storm - 海量处理引擎
Storm 是一个实时处理引擎。它接近于 map reduce,只是它一直在运行。因此是实时的。如果您需要这样的事情,您可以在批处理结束时使用并行工作单元处理数据和累积数据。风暴中使用的术语是“螺栓”和“喷口”。可以配置螺栓和喷口在称为“拓扑”的一个单元中运行
但真正的问题是确保一次性处理。这意味着,你如何保证从 Kafka 队列中只读取一次消息并成功处理。如果消息在处理过程中抛出异常并且您想再次重新处理该消息,会发生什么情况。
Storm 对 Spouts 和 Bolts 进行了抽象,称为三叉戟。它就像 Hadoop 的 Pig。其具体实现称为“OpaqueTrident”。 Opaque Trident Spout 保证只处理一次,最新风暴版本的官方发行版附带一个“OpaqueTridentKafkaSpout”。我们使用它并保证只处理一次来自 Kafka 的消息。
另一个重要的收获是如何处理失败的处理。警告是抛出“new FailedException()”。失败的异常不会将消息标记为已处理,因此它们将被重新处理。这保证了当由于网络问题或类似用例而丢失与数据库的临时连接时,您不会丢失消息。但要小心处理并确保您不会因为正在重新处理消息而写入重复数据。
这些是我们系统的经验教训。它是一只巨大的野兽,但如果使用得当,它就像一种魅力。
希望能帮助到你。
谢谢,
悉尼