基础功能:Topic、分区、订阅等基本功能是如何实现的?

64 阅读4分钟

静态 / 动态配置的实现

静态配置信息一般以 YAML、JSON、Properties 等格式存,在服务启动时加载配置文件到内存当中,进行业务逻辑处理。

动态配置是指服务可以从某个地方动态加载配置信息。一种是基于第三方组件,另一种是基于本地文件。

基于 ZooKeeper 是指我们可以在 ZooKeeper 创建持久节点存储配置信息,然后 Broker 通过 ZooKeeper 提供的 Watch 机制来监听节点。当节点内容变更时可以及时感知,并进行后续的处理。

基于本地文件的思路是指在代码实现中,通过代码技巧监听本地配置文件的变更,只要本地文件变更了,就进行配置变更的操作。

集群和节点元数据的设计

集群元数据是用来保存集群维度的一些基本信息。最简单的集群元数据一般只需要包含集群 ID 和集群名字两个信息。

{
  "ClusterId":"bmfursdfk",
  "ClusterName":"Trade"
}

集群维度的元数据需要持久化存储,所以我们可以在 ZooKeeper 上创建持久化节点 /Cluster 来存储集群的的元数据。

节点元数据是用来保存 Broker 维度的一些基本信息。Broker 元数据一般至少要包含节点的唯一标识 BrokerID、节点的 IP、节点监听的端口 3 个字段。

{
  "BrokerID":1, //BrokerID的类型可以是Int型,也可以是字符串型,区别不大。
  "BrokerIP":"192.2.1.1",
  "BrokerPort":8901
}

节点的元数据存储一般有这样两个思路:

  • 所有 Broker 元数据都存储在一个 ZooKeeper Node,比如 /node
  • 每个 Broker 元数据独立一个 ZooKeeper Node 存储,比如 /nodes/broker1、/nodes/broker2 等

目前业界主要使用的是第二个思路,主要的原因是:Broker 元数据分开存储方便管理,避免节点间相互影响,也可以避免单个 ZooKeeper Node 的数据量过大存不下。

Topic 和分区的支持

在消息队列中 Topic 和分区是必须的概念,Topic 是用来组织分区的逻辑概念,分区用来存储实体的消息数据。

Topic 至少需要 TopicID、Topic 名称、分区和副本在集群中的分布 3 个元素。

{
    "TopicID": "shlnjdlfsakw",
    "TopicName": "test",
    "Replica": [
        {
            "Partition": 0,
            "Leader": 0,
            "Rep": [
                0,
                1
            ]
        }
    ]
}

我们可以在 ZooKeeper 集群中创建一个持久化的节点来存储 Topic 的相关信息。

消费分组(订阅)的管理

根据“消费分组 + Topic + 分区号”三元组来记录消费进度,一般存储的格式会用 JSON 格式或者自定义的行格式:

// 存储格式:
{
  "GroupName":"group1",
  "TopicName": "topic1",
  "Partition":0,
  "Offset":0
}

// 行格式
group1,topic1,0,0

最常用的保存消费进度的思路有存 ZooKeeper、存本地文件、存内部的 Topic、存其他存储引擎 4 种思路。

  1. 存 ZooKeeper 是当前架构下一种最简单直观的方法。通过在 ZooKeeper 中为每个消费分组创建一个持久化的节点,比如 /groups/group1,来保存每个消费分组的消费进度信息。

  2. 存本地文件也是一种常用的思路。这种方案需要选择合适的数据结构来存储数据,以便进行高效的写入和更新。

  3. 存内部的 Topic 是指在集群中创建一个特殊名字的 Topic,比如 consumer_group_offset。此时这个 Topic 不允许普通用户读写,只允许用来保存消费进度。提供有压缩功能的 Topic,即 Topic 支持根据消息的 Key 对消息进行压缩。根据消息的 Key 进行压缩,只保留最后一条数据。

  4. 存其他存储引擎是指将消费进度存储在其他的存储引擎中,比如 MySQL、Redis 或其他第三方引擎等。这种方案本质上和存 ZooKeeper 的思路是一样的。得单独为存储消费进度信息而引入一个存储组件,这样会增加系统复杂度,还要考虑第三方组件的稳定性问题。

目前 Kafka 的消费位点保存方案最开始用的是存 ZooKeeper,后来用的是存内部的 Topic 的方案。RocketMQ 用的是存本地文件的方案。Pulsar 用的是存其他存储引擎 BookKeeper 的方案,Pulsar 能用这种方案的原因在于它是存算分离的架构,天然自带了存储引擎 BookKeeper,因此不会因为存储消费进度而引入额外的复杂度和成本。


此文章为11月Day27学习笔记,内容来源于极客时间《深入拆解消息队列 47 讲》