Back to Rocketmq

The Scope Of Static Topic

docs/cn/statictopic/The_Scope_Of_Static_Topic.md

latest5.1 KB
Original Source

Version 记录

时间主要内容作者
2021-11-01初稿,探讨Static Topic的Scope视线范围dongeforever

中文文档在描述特定专业术语时,仍然使用英文。

需求背景

RocketMQ的集群设计,是一个多集群、动态、零耦合的设计,具体体现在以下地方:

  • 一个 Nameserver 可以管理多个 Cluster
  • Broker 与 Cluster 之间是弱关联,Cluster仅仅只是一个标识符,主要在运维时使用来界定Topic的创建范围
  • 开发用户对 Cluster 无感知
  • 不同 Broker 之间没有任何关联

这样的设计,在运维时带来了极大的便利,但也带来了一个问题:

  • Topic 的队列数无法固定

基于 Logic Queue 技术而实现的 Static Topic,就是用来解决『固定队列数量』的问题。

但这个『固定』要到何种范围呢?是一个值得探讨的问题。

从理论上可以分析出来,有以下三种情况:

  • 单集群固定
  • 多集群固定
  • 全网固定

单集群固定

一个 Static Topic,固定在一个 Cluster 内漂移。 不同的 Cluster 内,可以拥有相同的 Static Topic。 对应MessageQueue的Broker 命名规范为:

__logic__{clusterName}

多集群固定

一个 Static Topic,固定在特定的几个 Cluster 内漂移。 没有交集的Cluster集合之间,可以拥有相同的 Static Topic。 对应MessageQueue的Broker 命名规范为:

__logic__{cluster1}_{cluster2}_{xxx}

全网固定

全网是指『同一个Nameserver内』。 一个 Static Topic,不与特定Cluster绑定,同一个Nameserver内,全网漂移。 同一个Nameserver内,只有一个同名的 Static Topic。 对应MessageQueue的Broker 命名规范为:

__logic__global

为什么要引入Scope

直接全网固定不就好了吗,为啥还要引入Scope呢? 主要原因是,不想完全放弃 RocketMQ 『多集群、动态、零耦合』的设计优势。 而全网固定,则意味着彻底失去了这个优势。

举1个『多活保序』的场景:

  • ClusterA 部署在 SiteA 内,创建 Static Topic 『TopicTest』,有50个队列。
  • ClusterB 部署在 SiteB 内,创建 Static Topic 『TopicTest』,有50个队列。

对Nameserver稍作修改,支持传入标识符(比如为scope或者unitName),来获取特定范围内的 Topic Route。

正常情况下:

  • SiteA 的Producer和Consumer 只能看见 ClusterA 的 MessageQueue,brokerName为 "__logic__clusterA"。
  • SiteB 的Producer和Consumer 只能看见 ClusterB 的 MessageQueue,brokerName为 "__logic__clusterB"。
  • 机房内就近访问,且机房内严格保序。

假设 SiteA 宕机,此时对Nameserver发指令允许全网读,也即忽略客户端传入的 Scope或者unitName 标识符:

  • SiteB 的 Producer 仍然看见并写入 ClusterB 的 MessageQueue,brokerName为 "__logic__clusterB"
  • SiteB 的 Consumer 可以同时看见并读取 ClusterA 的 MessageQueue 和 ClusterB MessageQueue, brokerName为 "__logic__clusterB" 和 "__logic__clusterA
  • 在这种场景下,Consumer 在消费 ClusterB 数据的同时,同时去消费 ClusterA 未消费完的数据

不同地域的客户端,看见不同Scope的元数据,从而访问不同Scope的节点。

全球容灾集群

RocketMQ 多个集群的元数据可以无缝在Nameserver处汇聚,同时又可以无缝地根据标识符拆分给不同地域的Producer和Consumer。 这样一个『元数据可分可合』的设计优势,是其它消息中间件所不具备的,应该值得挖掘一下。
引入以下概念:

  • 融合集群,共享同一个Nameserver的集群之和
  • 单元集群,clusterName名字一样的集群,不同单元集群之间,物理隔离
  • namespace,租户,逻辑隔离,只是命名的区别

如果单元集群部署在异地,所形成的『融合集群』,就是全球容灾集群:

  • 客户端引入 scope 或者 unitName 字段,默认情况,不同 scope或者unitName 获取的都是单元集群的元数据
  • 顺序性,关键在于 固定Producer端可见的队列,单元内的队列是固定的,因此可以保证单元内是顺序的
  • Consumer 端按照队列消费,天然是顺序的
  • 正常情况下,单元内封闭,也即单元产生的数据在同单元内消费掉
  • 灾难发生时,改变元数据的可见性,允许读其它 单元集群 未消费完的数据,也即跨单元读
  • 跨单元读,是指读『其它clusterName』的队列,不一定是远程读,如果本单元有相应的Slave节点,则直接本地读

设计目标

Static Topic 实现 单集群固定 和 全网固定 两种Scope,以适配『全球容灾集群』。 多集群,暂时没有必要。

一期只实现 全网固定 这个Scope,但在格式上注意兼容

SOT 增加 Scope 字段

{
"version":"1",
"scope": "clusterA",
"bname": "broker02" //标记这份数据的原始存储位置,如果发送误拷贝,可以利用这个字段来进行标识
"epoch": 0, //标记修改版本,用来做一致性校验
"totalQueues":"50",  //当前Topic 总共有多少 LogicQueues
}

scope字段:

  • 单集群固定,则就是 Cluster 名字
  • 全网固定,则为常量『global』