跨地域复制(geo-replication)在分布式消息系统里属于那种“平时不起眼、一到大促或机房出状况就决定生死”的能力。Pulsar 把复制做成了原生特性,不依赖外部组件,也不需要你手动搭一套“镜像”管道,但你真要把它用好,光会敲几条命令远远不够。这篇文章我打算用 8 张关键示意图,把 Pulsar 跨地域复制的整体结构、消息流转、订阅状态、故障切换和排障思路完整串一遍,适合正在选型消息中间件、或者已经在生产环境维护 Pulsar 集群的架构师和运维同学。看完之后,你至少能回答三件事:复制到底把什么同步过去了、复制滞后怎么看、切机房的时候需要做什么。
1. 跨地域复制到底在解决什么问题
1.1 单集群的天然局限
先聊点大实话。一个 Pulsar 集群无论部署得多强壮,它都有一个物理边界:所有 broker、bookie 都跑在同一个机房或同一个云区域内。只要这个区域整体出故障,比如光缆被挖断、核心交换机绕路、大面积断电,整个集群就算业务代码一行没改,也照样不可用。
单集群模式的第二个问题不是“挂掉”,而是“离得远”。用户的请求从上海打到部署在美东的集群,光往返延迟就接近 200 毫秒,对实时交互类的业务来说根本没法接受。你当然可以在每个区域各部署一套独立集群,但业务系统就得自己处理“两套集群数据怎么保持一致”这个问题,这通常比想象中复杂得多。
跨地域复制的核心价值,就是让多个物理上分离的 Pulsar 集群在逻辑上组成一个整体:每个集群既可以独立承担本区域的读写流量,又能把数据异步同步到其他集群。这样既解决了就近访问的延迟问题,也给了你一个真正的多活容灾方案。
1.2 跨地域复制不等于备份
很多刚接触 Pulsar 的人会把复制和备份混在一起,这是两个维度的东西。备份是定期拍照,恢复点取决于上次快照时间,RPO(恢复点目标)可能长达小时级;复制是持续直播,消息从生产者落地的瞬间就开始往远端集群转发,正常情况下远端集群和源集群的差量只有秒级甚至毫秒级。
再拿 Kafka 生态来对比一下:Kafka 的跨地域镜像通常依赖 MirrorMaker 这类外部组件,它本质上是消费源集群消息再写回目标集群,配置和运维都比较重,而且默认配置下消费组状态并不会跟着镜像走。Pulsar 的复制是 broker 内置的 Replicator 机制,消息从 entry 落盘之后由副本生产者直接写入远端集群,不需要额外部署独立进程。
还有一个容易忽略的点:备份关心的是“能不能恢复”,复制关心的是“能不能继续服务”。Pulsar 跨地域复制做完之后,每个集群都有完整数据副本,任何一个地域的消费者都可以读本地消息,任何一个地域的生产者写进去的消息最终都会到达其他地域。这意味着你可以在任何一个地域对外提供读写服务,这才是真正的多活。
1.3 关键概念先打个底
在展开 8 张图之前,先建立几个认知锚点,后面不会懵。
- Pulsar Instance:一组共享同一套全局配置存储(通常称为 Global ZooKeeper 或 configuration store)的 Pulsar 集群集合。跨地域复制的前提就是这些集群属于同一个 Pulsar Instance。
- Replicator:运行在每个 broker 进程内部的一个组件,负责把本集群的消息复制到远端集群,同时也会从远端集群拉取消息到本集群。它是一个桥接角色。
- Topic 策略:在 namespace 级别或 topic 级别可以设置“允许复制到哪些集群”,这是复制开关的核心配置。
- 复制滞后(replication backlog):源集群已经生产、但远端集群还没有消费复制的消息量。这是运维时最重要的观察指标之一。
有了这些基础,下面 8 张图就好解释了。我会一张一张拆,讲清楚每张图背后“为什么这么设计”以及实际踩坑的地方。
2. 八张图拆解跨地域复制的核心机制
2.1 图 1:Pulsar Instance 的全局拓扑视图
第一张图先画整体架构。假设有两个物理集群:集群 A 部署在某云华北区域,集群 B 部署在某云华南区域。它们各自都有自己的 broker 节点、bookie 存储节点和本地 ZooKeeper,但它们共同连接一个全局 ZooKeeper 集群,这个全局 ZooKeeper 就是 Pulsar Instance 的控制面锚点。
这张图要表达的关键信息是:跨地域复制不是两个集群之间的点对点直连,而是在一个更大的逻辑边界里做消息路由。每个集群仍然完全自治,broker 只认识自己集群内部的 bookie;但 topic 的元数据、复制策略、租户和 namespace 信息都会注册到全局 ZooKeeper 上,让所有集群对“世界上有哪些 topic、它们属于谁”这件事达成共识。
从实际部署角度看,这种设计带来一个很实际的好处:控制面和数据面分离。你不需要为了配置一个复制链路去修改每个 broker 的本地文件,只要在全局配置存储上更新策略,所有集群的 broker 都能感知。后续你新增第三个集群时,也不需要动已有集群的代码,只要让新集群接入同一个全局 ZooKeeper,再设置好对应的 namespace 复制策略,就能进入复制体系。
我当时第一次搭这种结构时最大的感受是:集群数量翻倍之后,复杂度并没有呈指数上升,因为全局 ZooKeeper 把“集群间如何互相发现”这件事统一管起来了。运维上需要重点保护的就是这个全局 ZooKeeper,它一旦出问题,整个 Pulsar Instance 的控制面都会受影响,所以它的节点要有独立故障域,千万别跟某个地域的本地集群绑死。
2.2 图 2:一条消息从写入到复制的完整流转
第二张图往下钻一层,看一条消息从生产者到远端消费者之间到底经过了哪些节点。
生产者在集群 A 写入消息,数据流是先到 broker A 的入口,然后追加写入本地的 bookie 完成持久化。这里注意,生产写入成功不等价于远端复制完成。broker A 返回给生产者的 ack 只代表“你这条消息已经在集群 A 落了地”,复制是异步的。
在消息完成本地持久化之后,Replicator 组件开始介入。你可以把 Replicator 理解成一个双重角色的线程:对源集群来说,它是一个消费者,按顺序读取本集群中已经落盘的消息;对目标集群来说,它又是一个生产者,把读到的消息重新包装并写入远端集群。消息到了集群 B 之后,再由 B 自己的 broker 追加到 B 的 bookie。最终,下游消费者通过订阅去读取集群 B 中这条消息。
这张图揭示了一个很重要的特性:跨地域复制是有序的,而且在分区粒度上保持严格顺序。Replicator 是按游标顺序消费源 topic 的消息,再按同样的顺序生产到目标 topic,所以每个分区内的消息顺序在复制后不会乱。实践里我见过有人担心复制会像网络抖动一样把顺序打乱,其实不会,这是 Pulsar 做复制时的一个硬性保证。
同时也可以看到一个容易被忽略的问题:跨地域复制会吃一份磁盘和网络资源。每条消息在本集群写一遍,在被 Replicator 读出来再写到远端集群时,远端又写一遍。如果复制到多个集群,就是多份存储多份带宽。规划容量时一定要把这个放大系数算进去,不能只看单集群的写入流量。
2.3 图 3:复制策略的控制面同步
第三张图聚焦“控制面”:你执行一条配置命令之后,复制策略是怎么在整个 Pulsar Instance 里生效的。
在某个 namespace 上设置set-clusters,把集群 A 和集群 B 都加入复制范围之后,这条策略会写入全局 ZooKeeper。每个集群中的 broker 都会通过监听机制感知到这个变更,从而知道自己需要为这个 namespace 下的所有 topic 启动复制链路。
这里要强调一个细节:复制的配置粒度是 namespace 级别,而不是 topic 级别。虽然 topic 级别也可以覆盖配置,但日常使用中绝大多数团队都会在 namespace 级别统一管理。比如订单业务的所有 topic 放在一个名为orders的 namespace 下,需要多活时只要把这个 namespace 的复制范围设成 A 和 B 即可,后续在该 namespace 下新增 topic 也会自动继承复制策略,不需要手动逐条配置。
控制面同步是一个典型的“最终一致”过程,它不是强实时的。配置下发到所有 broker 通常只需要几秒,但如果某个 broker 与全局 ZooKeeper 连接不稳定,这个 broker 上的复制链路可能不会如期启动。所以每次变更复制策略之后,我都习惯在目标集群的 broker 日志里确认一下 namespace 策略已经加载,而不是只盯着命令行工具返回的成功提示。
另外,不要以为复制策略只影响数据面。复制策略变化还会影响 topic 的 ownership 接管逻辑。比如某个集群从复制范围中被移除后,该集群上的相关 topic 副本消息不会被删除,但新的消息不会再同步过来,已经存在的数据可以当作历史归档保留。
2.4 图 4:复制方向的判定与循环消息防护
第四张图是我认为最值得看的一张,它说明了 Pulsar 如何避免“消息在两个集群之间无限循环复制”。
画一张复制环:集群 A 收到本地生产者的消息,Replicator 把它复制到集群 B;集群 B 的 Replicator 看到这条来自 A 的消息后,又会不会立刻把它复制回集群 A?如果没有任何防护,这条消息就会在 A 和 B 之间来回跑,形成无限循环,直到消息过期或集群崩溃。
Pulsar 的解决方案是给复制消息打上专属标识。Replicator 在向目标集群生产消息时,会使用一个特殊的 producer 标识,这个标识带有来源集群的信息。接收端的 broker 遇到这种标识的消息时,就能直接识别出“这条消息是复制消息,不需要再往来源集群转回去”,于是循环被切断。
实际运维中我还遇到过另一个与去重相关的问题:当复制链路发生切换,比如某个 broker 故障导致 Replicator 在另一端重建,可能会有极小概率出现同一条消息被复制两次。这种重复不会破坏顺序,但会导致下游消费者拿到重复数据。因此我一般会建议下游消费逻辑里对关键业务消息做幂等处理,依赖消息中的唯一标识或业务主键做去重,而不是把“跨地域复制一定不重复”当作绝对前提。
这张图反过来也提醒了一点:复制的方向不是“自动对称”的,它完全由 namespace 上的复制集群集合决定。如果你只在某个集群上配置了复制到另一个集群,另一个集群没有配置回来,那数据方向就是单向的。这个特性可以做一些特殊用法,比如把生产环境数据单向复制到测试区域做联调,但别让测试区域的改动污染生产。
2.5 图 5:订阅状态在哪里复制,哪里不复制
第五张图要回答一个很多人搞不清的问题:消费者已经消费到的位置,会随着跨地域复制一起同步吗?
答案是不会。Pulsar 复制的是消息本身,不是消费者的游标状态。每个集群上的订阅都是独立管理的。比如集群 B 上有一个订阅group-order-consumer,这个订阅的消费进度只存在集群 B 的 bookie 和 ZooKeeper 里,它不会被复制回集群 A,也不会影响集群 A 上同名订阅的游标。
这意味着一套业务逻辑切换地域时,新地域的消费者组必须从自己本地的订阅状态开始消费。如果集群 B 是刚接入复制的新集群,那么 B 上的消费者如果没有提前创建订阅,它会从最早的消息开始读,或者从你设置的位置开始读,这可能导致大量历史消息被重新消费一遍。
理解了这张图,你才能正确设计故障切换流程。切流量之前,不能只看“消息有没有复制过去”,还要确认目标集群的订阅游标是不是已经追到了源集群当时的写入位置。如果两端游标不一致,消费者就会各自消费不同的数据,而你以为大家在同一个进度上。
实际场景中我经常看到团队在切地域时直接改一下 DNS 或 VIP 就把消费者切过去,结果目标集群的消费者从很久之前的位置开始消费,业务被一堆旧数据冲刷。正确的做法是在切换前先确认目标集群订阅已经追上复制进度,或者干脆采用“调新订阅起始位置”的方式,手动把游标放到一个明确的时间点。
2.6 图 6:生产切换与故障恢复的时序逻辑
第六张图看的是故障场景下的时序。假设集群 A 整体不可用,你决定把所有生产流量切到集群 B。这时候要清楚一个事实:复制是异步的,集群 B 上最后一批消息可能只复制到了第 N 条,而集群 A 真实接收到的消息已经到第 N+1000 条。A 崩溃时,这 1000 条还没复制出去的消息就丢了,这是跨地域异步复制的固有窗口,没法完全消除。
不少人对这个窗口感到焦虑,总想通过“同步双写”之类的方式做到零丢失,但那样做会把写入性能拖垮。Pulsar 的跨地域复制走的是性能优先的异步路径,你要做的不是追求强一致,而是根据业务容忍度决定接受多大的 RPO。像订单通知、日志同步这类场景,秒级甚至分钟级滞后完全可接受;而涉及账户余额变更的,就不应该只依赖跨地域复制,建议业务层再做一次本地确认或对账。
切换过程还有第二个陷阱:故障恢复后,集群 A 重新上线,这时它的数据落后于 B。如果你贸然把生产流量切回 A,A 上缺少的这段时间内的消息并不会自动从 B 补回来,除非你配置了 A 从 B 复制。所以在切回之前,最好先让 A 以复制目标的方式从 B 同步数据追平进度,然后才把读流量、写流量逐步回切。这张图画的就是这个回切和追平的时序:切出要果断,切回要谨慎。
2.7 图 7:复制延迟与网络带宽的约束关系
第七张图看性能模型。跨地域复制本质上是把“本地写盘”的结果持续搬到远端,因此它受制于两个瓶颈:网络带宽和 Replicator 的处理能力。
当源集群的生产速率超过网络带宽时,Replicator 消费上游消息的速度就会慢于生产速度,复制 backlog 开始增长。这个 backlog 在 Pulsar 里可以直接从 topic 统计信息中看到,体现为复制订阅的积压数量。经验上,如果 backlog 长期高水位运行,优先要查的是带宽是否被打满,而不是去调 broker 内存参数。
第二个瓶颈是 Replicator 所在 broker 的资源竞争。Replicator 毕竟跑在 broker 进程里,如果这个 broker 同时承担了大量本地生产和消费连接,CPU 和内存都会紧张。实际操作中,我会把复制链路比较重的集群的 broker 数量适当留点余量,或者在独立的管理分区运行复制压力测试,摸清单 broker 的复制吞吐上限。
带宽控制这块,可以根据需求在配置里限制复制速率,避免为了跨地域复制把本地生产的网络带宽吃掉。具体参数在不同版本里名字略有差异,配置前以官方文档为准。我的建议是:跨地域复制最好走独立的网络通道或至少独立的 QoS 队列,这样即使复制流量打满带宽,也不会影响本集群核心链路的延迟。
2.8 图 8:从配置到监控的完整链路
最后一张图是一张全景图,把前面七张图串成一条从“部署配置”到“日常监控”的完整链路。
链路起点是规划 Pulsar Instance:先初始化全局配置存储,再逐一加入地域集群。然后是 namespace 复制策略激活,此时每个 broker 上的 Replicator 会为相关 topic 建立复制消费者和复制生产者。接下来是日常运行:生产者写入、本地落盘、Replicator 异步搬运、远端落盘、远端消费者读取,整个过程持续进行。最后一环是监控:重点指标包括复制 backlog、Replicator 所在 broker 的资源水位、远端集群登录后的 topic 消息最新发布位置,以及复制链路是否断开的告警。
要特别说明的是,监控这条链路里最容易遗漏的是“静默中断”。有时候复制链路会因为网络抖动、broker 重启等原因中断,但消息还在正常生产,源集群的 broker 日志里可能只有偶尔的报错,不仔细看根本发现不了。最有效的做法是对复制 backlog 设置告警,只要 backlog 超过阈值就立刻触发,比翻日志高效得多。
如果你能把第八张图画出来,整个跨地域复制的体系就都在脑子里了。后面配置和排障,无非是对着这张图找具体环节。
3. 从零配置一套跨地域复制的实操记录
3.1 环境准备与版本一致性
低风险的做法是让所有集群的 Pulsar 版本保持一致,避免协议差异带来的兼容问题。我见过版本跨度较大的集群之间复制出现莫名失败,查了半天是消息元数据字段解析不兼容。所以第一步建议把各区域集群的版本统一,再继续往下走。
网络维度需要保证集群之间的节点可以互相访问 broker 的 6650/8080 端口以及 bookie 的 3181 端口。如果存在安全组或防火墙,记得给 Replicator 所在节点放通这些端口,否则复制链路建连时会卡在 TCP 层。跨地域网络延迟通常比同一机房高不少,建议调大 broker 与远端 broker 之间的 keepalive 参数,避免连接被中途断开。
另外强烈建议开启全局时钟同步,把每个集群所有节点都接入同一套 NTP 服务。Pulsar 跨地域复制的消息顺序不依赖物理时钟,但时间戳相关的功能,比如消息保留时间的过期清理,在时钟偏差过大时会出现奇怪行为,比如某集群认为消息已经过期把它删了,另一个集群认为还没过期。
3.2 初始化集群元数据与配置复制策略
初始化流程可以分为三步。第一步是创建全局 Pulsar Instance 的元数据,这一步通常在全局 ZooKeeper 上执行,命令类似pulsar initialize-cluster-metadata,需要指定集群名、ZK 地址、broker 地址等参数。第二步是依次初始化各区域的集群元数据,把每个集群注册到这个 Pulsar Instance 中。第三步是配置 namespace 的复制策略。
假设有两个集群,名称分别是cluster-a和cluster-b,租户是my-app,namespace 是my-app/orders,那么配置复制的核心命令大致如下:
pulsar-admin namespaces set-clusters \ --clusters cluster-a,cluster-b \ my-app/orders执行完成后,该 namespace 下的所有 topic 都会在cluster-a和cluster-b之间互相复制。注意这里用的是set-clusters,而不是add-clusters,执行前想清楚最终范围,避免误操作把某个集群移出复制范围。
如果你只想让部分 topic 复制,就需要在 namespace 级别先设置一个较小的范围,再针对具体 topic 用set-clusters覆盖。我个人的习惯是尽量用 namespace 级别,避免每个 topic 单独配置,后续维护成本低。
3.3 验证复制是否真正生效
配置完之后不要急着切流量,先做一个最小验证。在集群 A 的生产者上发送一批测试消息,然后在集群 B 的消费者上去读。确认 B 能收到之后,还要检查一下复制方向是否符合预期。比如你只想让 A 复制到 B,那 B 上写一条消息,A 就不能收到。
最直观的验证方式是查看 topic 的统计信息,命令类似:
pulsar-admin topics stats persistent://my-app/orders/order-events在输出中会看到与复制相关的订阅信息,比如名为replicator的复制订阅,它后面的 backlog 数字就能直接反映复制是否追赶上了。如果 backlog 持续增长,而远端消费者已经能读到一些新消息,说明复制链路已经建立,只是吞吐跟不上;如果 backlog 完全没有变化,先怀疑复制策略有没有生效,再去翻 broker 日志。
这里有一个很容易踩的坑:测试时不要用persistent://之外的 topic 类型来做验证。跨地域复制对持久化 topic 的支持是最完善的,虽然说非持久化 topic 在某些版本也有部分复制支持,但它没有稳定的落盘和游标机制,不好排查。另一个建议是测试消费者尽量新起一个订阅,不要复用现有订阅,避免消费到一个已经推进过很多的位置,测试完再删掉这个临时订阅。
4. 常见问题与排查技巧实录
4.1 复制延迟突然增大该怎么定位
复制延迟增大是最常见的故障表象,但根因五花八门。我的排查顺序是:先看网络,再看 broker 资源,最后看消息体特征。
- 看网络:在两个集群之间做
iperf或ping测试,确认带宽和丢包率。跨地域传输如果丢包严重,TCP 重传会吃掉大量有效带宽。 - 看 broker 资源:用监控看 Replicator 所在 broker 的 CPU、内存、网络队列,判断是不是被本地读写流量挤占。
- 看消息体特征:如果消息体特别大,比如单条 1MB 以上,虽然整个系统吞吐量不高,但 Replicator 逐个搬运大消息的开销并不小。
有时候延迟增大其实只是“表面错觉”。比如生产端瞬时写入尖峰叠加复制消费跟不上,backlog 会迅速拉高,但消息体积不大、网络也没问题,那只要生产流量恢复平稳,Replicator 会自动追平,不需要人工干预。真正需要关注的是那种长期持续增长、没有回落迹象的 backlog。
4.2 复制链路状态正常但远端读不到消息
链路状态正常、远端却读不到新消息,这个问题的排查点往往在订阅而不是复制本身。先确认远端消费者订阅的是哪个 topic、哪个订阅名,再看这个订阅的游标位置。如果订阅是在复制策略配置之前创建的,它可能停在某个很旧的位置上,新消息虽然已经在 topic 里,但游标不动,消费者自然读不到。
还有一种情况是 Replicator 正常复制过来了,但消息因为保留策略被提前清理了。比如你在源集群配置了一条很短的消息过期时间,消息在源集群写入之后、还没来得及复制到远端,就在本地被 cleanup 线程删掉了,那复制也没有机会执行。跨地域复制场景下,源集群的消息保留时间要大于你预期的复制滞后时间,否则会出现“追不上、追上了也没用”的局面。
为了避免这种问题,我通常会建议把参与复制 namespace 的消息保留策略调长一点,或者至少确保复制订阅存在。Pulsar 的复制订阅本身算是一种订阅,它会在一定程度上阻止过期消息被清理,只看具体版本对无消费者订阅的清理策略是否生效。
4.3 故障切换后如何确认数据是否补齐
故障切换之后最常见的动作就是“急着回切”。如果你想确认数据是否补齐,有一个比较可靠的检查方式:对比源集群和目标集群同一个 topic 的最新消息发布位置。在目标集群上,如果 Replicator 的复制订阅位置已经追上 topic 当前写入位置,也就是复制 backlog 归零,那可以认为当前时刻两边的数据是持平的。
但这里“持平”只代表某个检查瞬间,不代表没有任何丢失。异步复制天然存在一个闭环窗口,源集群在宕机瞬间可能还有消息没复制出去,这些消息会永久丢失。所以切换之后要做的是评估这次故障实际丢了多少消息,而不是假装整个过程零丢失。
有一个经验是保留切换当天的生产日志或消息序号记录,事后可以精确对账。如果业务侧有消息全局序列号,只需要对比源集群最新序号和目标集群最新序号,差值就是大致丢失的消息数量。有了这个数字,你就知道是否需要做一次业务数据补偿,而不是盲目相信监控面板上显示的健康状态。
4.4 集群永久下线后如何清理复制关系
下线一个集群比新增一个集群容易踩坑,因为配置可以删,但历史数据和处理逻辑很难一下清干净。假设集群 A 永久下线,你需要在全局配置中把 A 从所有 namespace 的复制集群列表里移除,然后删除 A 的集群元数据。这个操作会让其他集群上的 Replicator 不再尝试连接 A,也停止向 A 复制新数据。
但 A 上已有的消息副本还在它的 bookie 里,这些数据不会自动搬走。如果你有审计或归档需求,建议先单独导出保存;如果没有,等元数据删除后再清理节点上的存储目录。
我碰到过一种情况:集群被从复制范围移除后,其他集群的 broker 日志里还在报“连接拒绝”,排查后发现是旧配置缓存没有刷新,重启 broker 后自动恢复。如果遇到类似现象,重点关注各个 broker 是否都已经从全局 ZooKeeper 拿到了最新策略,必要时滚动重启一次让配置完全生效。
我把高频问题汇总成一张表,方便对照排查。
| 问题现象 | 可能原因 | 优先排查方向 |
|---|---|---|
| 复制 backlog 持续增长 | 跨地域带宽不足、Replicator 所在 broker 过载 | 网络探测、broker 资源监控 |
| 远端新消息缺失 | 复制策略未生效、订阅游标未推进 | broker 日志、topic 统计 |
| 复制链路反复断开 | 防火墙端口未放通、连接 keepalive 超时 | 安全组规则、日志中连接报错 |
| 消息出现重复消费 | 复制切换瞬间的重复复制 | 消费端幂等、消息唯一标识 |
| 切换后读不到最新消息 | 目标集群订阅滞后、消息未被复制 | 检查复制订阅 backlog |
| 旧集群下线后日志持续报错 | 配置缓存未刷新 | 滚动重启 broker |
5. 性能影响与生产落地建议
跨地域复制的性能开销常常被低估。每条消息等于多了一轮“读取本集群消息、跨地域网络传输、写入远端 bookie”的完整链路。尽管 Pulsar 的 Replicator 做了比较多的优化,比如批量读取、批量写入和异步流水线,但整体吞吐上限仍远低于单集群内部生产消费。
评估容量时,不要只按峰值 QPS 来算,还要看消息平均大小和复制目标数量。如果一条消息平均 10KB,每秒 10 万条,复制到一个远端集群就等于每秒多传输 1GB 数据。这个数字会让很多人的带宽预算瞬间破防。更稳妥的方式是在预发环境搭建两个小集群,模拟跨地域延迟,先压测出复制吞吐基线,再用基线数据推算生产规模。
消费者线程数也会影响复制链路。Replicator 在目标集群一侧以生产者身份写入时,受限于目标集群 broker 的处理能力。同一个租户里如果还有其他重负载业务,需要考虑资源隔离。比较理想的部署方式是让复制流量大的集群和普通业务集群分离,但这要看公司整体资源规模,做不到完全隔离时,至少要把监控做到位。
还有一个容易被忽略的细节:消息压缩。跨地域复制传输的是消息体本身,如果生产者在源头启用了压缩,复制传输的字节数就会明显下降。像 JSON 类型的业务数据压缩率通常很可观,能有效降低带宽压力。但压缩会增加生产端 CPU 开销,要结合实际消息特征做取舍,不能一刀切地全都压缩。
关于生产落地,我倾向于按业务重要性分层设置。核心交易链路可以开启跨地域复制并且高频监控 backlog;日志和指标类数据可以不开复制,通过其他数据管道汇总;中间态数据比如临时缓存、Session 信息,则完全不需要参与跨地域复制。这样既控制了复制成本,也不会把所有鸡蛋放在同一个复制链路里。
6. 一点个人经验和补充
这套 8 张图的拆解方法,其实是我在多次跨地域故障复盘中沉淀出来的。刚开始接触 Pulsar 跨地域复制时,我也觉得它就是“配置两个集群、然后消息自动过去”,直到有一次切机房时才发现,真正难的不是让消息飞过去,而是如何理解复制在哪里停止、消费从哪里继续、数据什么时候才算真正安全。
如果你只从这篇文章里带走一个概念,我希望是“复制的是消息,不是状态”。消息可以从一个集群搬到另一个集群,但消费游标、topic 的各个订阅状态、生产者的连接状态都只能在本地集群维护。理解了这个边界,你设计故障切换方案时就不会被“数据都复制过去了,为什么下游状态还是不对”这种问题困住。
另外给一个小技巧:开启跨地域复制之后,建议在每个参与复制的集群上为关键 topic 都建一个专门用于追踪的复制消费订阅,日常巡检时只需要看这些订阅的 backlog 是否在安全水位以内,不需要把每个 topic 的详细统计都翻开看。backlog 一旦出现持续增长,再针对具体 topic 深入排查。
调优方向不分先后,但有一个顺序建议:先保证日志和监控完整,再优化性能和配置。没有监控的跨地域复制就像蒙着眼睛开快车,速度再快,方向错了也只是加速事故。先把 8 张图的每一个环节都搭上监控,复制链路才算真正落地。