消息队列流处理后端微服务消息路由【免费下载链接】pulsarApache Pulsar - distributed pub-sub messaging system项目地址https://gitcode.com/gh_mirrors/pu/pulsar点击查看免费下载本文围绕 Apache Pulsar 的 PIP-157 提案展开讲清楚“单个命名空间内主题数量受元数据存储制约”这一问题的成因、分桶bucketing方案的总体设计、新增命名空间策略numberOfTopicBuckets的语义、桶分配的哈希规则与元数据目录示例并结合当前仓库中TopicResources、TopicName等源码位置说明该特性在元数据持久化链路上的具体挂载点帮助读者完整理解这一扩容方案的原理与落地边界。一、问题背景为什么单命名空间的主题数量存在上限Pulsar 可以管理数百万个主题但单个命名空间内的主题数量受到元数据存储的制约。提案文档pip/pip-157.md在 Motivation 一节给出了直接原因命名空间内的每个主题在 ZooKeeper 中对应一个节点。列出list主题意味着对某个节点做子节点列举list children而这一操作在主题数达到约 1 万10K时就会触碰到 ZooKeeper 的能力上限。从当前仓库源码可以印证这一元数据布局。pulsar-broker-common模块中集中管理/managed-ledgers路径访问的 TopicResources 类其列举持久主题的实现是public CompletableFutureListString listPersistentTopicsAsync(NamespaceName ns) { String path MANAGED_LEDGER_PATH / ns /persistent; return store.getChildren(path).thenApply(children - ...); }即给定命名空间tenant/namespace主题直接平铺在/managed-ledgers/tenant/namespace/persistent/之下。store.getChildren(path)一次返回该路径下所有子节点——这正是瓶颈所在主题越多该节点子节点越多ZooKeeper 的 getChildren 调用越慢、越容易失败最终限制单命名空间的主题规模。二、方案目标在 topic 节点前插入中间层bucketsPIP-157 的核心目标是在 topic 节点之前插入一层“桶”buckets中间层把原来的持久化路径/managed-ledgers/tenant/namespace/domain/topic改造为/managed-ledgers/tenant/namespace/domain/bucket/topic这样列举一个命名空间下的主题时不再需要对一个巨大的节点做 getChildren而是对若干个桶节点分别列举单节点的子节点规模被摊薄从而支撑单个命名空间内的主题数量大幅提升。提案中还明确了两个重要的设计约束默认关闭、按命名空间粒度启用。该特性默认处于关闭状态仅在创建命名空间时通过设置策略policy按命名空间启用。这样完全避免了对存量集群进行元数据迁移的需要——旧命名空间保持原有结构不变。桶与 bundle 没有对应关系。桶bucket只服务于元数据的列举性能与 Pulsar 中用于负载分担的 bundle 机制相互独立运维人员不需要也无法把两者对应起来理解。三、API 变化新增命名空间策略numberOfTopicBucketsPIP-157 在 API 层面的变化很小但很关键新增一个命名空间策略numberOfTopicBuckets。语义取值行为默认值 11不分桶完全保持命名空间当前的行为路径中无桶层分桶 1主题的元数据将存储在包含桶的层级路径下同时提案明确规定命名空间创建后用户不能修改桶的数量。这一约束来自“分桶意味着物理路径结构变更变更桶数等价于对全部主题元数据做一次原子搬迁”的事实考量后文的“被拒绝的替代方案”一节也呼应了这一点。值得说明的是基于对当前仓库的检索numberOfTopicBuckets这一策略尚未出现在代码库中pip/pip-157.md 是该特性的设计载体也就是说当前代码库快照处于该特性的设计/实施阶段。下文的实现分析均基于该 PIP 的设计描述并结合仓库中现有代码结构指出其接入点。四、实现方案对用户透明的持久化命名翻译PIP-157 在 Implementation 一节的总体原则是该特性对用户透明。客户端继续以domain://tenant/namespace/topic的形式引用主题Pulsar 在内部“按需”把它翻译成新的持久化命名含桶号。提案给出的实现机制是向TopicName.getPersistenceNamingEncoding提供桶号bucket number并据此计算出修改后的路径。当前仓库中该方法位于 TopicName.java现有实现按领域生成持久化相对路径public String getPersistenceNamingEncoding() { // The convention is: domain://tenant/namespace/topic // We want to persist in the order: tenant/namespace/domain/topic if (domain TopicDomain.segment segmentRange ! null) { return String.format(%s/%s/%s/%s/%04x-%04x-%d, tenant, namespacePortion, domain, getEncodedLocalName(), segmentRange.start(), segmentRange.end(), segmentId); } if (domain TopicDomain.topic) { return String.format(%s/%s/%s, tenant, namespacePortion, getEncodedLocalName()); } return String.format(%s/%s/%s/%s, tenant, namespacePortion, domain, getEncodedLocalName()); }对普通 persistent/non-persistent 主题编码结果形如tenant/namespace/persistent/topic即四段形式TopicNameTest 中的断言assertEquals(name.getPersistenceNamingEncoding(), prop/ns/persistent/ encodedName)正是对这一格式的固化。PIP-157 的改造点即在这里为该编码方法引入桶号参数使普通主题的编码结果变为tenant/namespace/domain/$N/topic形式的五段路径桶号大于 1 时。与编码对应的反向解析方法是 fromPersistenceNamingEncoding它目前按段数区分四种情况4 段标准格式、5 段且第三段为segment分段主题、5 段遗留 V1 格式tenant/cluster/namespace/domain/topic、其余抛异常。从源码结构看引入桶层后这里需要新增对“五段且第三段为 domain 的桶化路径”的识别这是实现时需要处理的兼容性细节之一。提案同时强调元数据存储metadata store的工作方式不受影响。pulsar-metadata层的抽象不感知“桶”的语义分桶只体现在 broker 侧计算出的路径上。4.1 桶的分配规则哈希取模主题分配到哪个桶采用确定性规则以主题名称的哈希码hash code绝对值对桶数取模即abs(topicName.hashCode()) % numberOfBuckets。这一规则保证了同一主题在任意 broker、任意时刻计算出的桶号一致因而对主题的创建、存在性检查、列举等操作都指向同一个物理路径无需额外的“桶路由表”或一致性协调。4.2 主要工作量让命名空间策略在“计算持久化命名处”可用提案指出该特性的大部分改动量在于让命名空间策略namespace policies在所有需要计算持久化命名的位置可用——因为要知道某主题进哪个桶就必须先知道其命名空间的numberOfTopicBuckets值。在需要列举命名空间内主题的场景包括检查单命名空间主题数量上限是否已触达新的中间层会带来额外开销每个桶一次元数据存储请求取代原来的一次请求。这是设计者明示的性能权衡——用“桶数次 getChildren”换取“单个 getChildren 不再触碰 ZooKeeper 大节点上限”在桶数通常较小的前提下开销可控。4.3 Schema 存储无需改动由于瓶颈在“列举主题”通过 managed ledgers 的路径完成提案明确不需要修改 schema 存储并且当前存储在/managed-ledgers/tenant/namespace/domain/topic下的数据结构与内容保持不变只是在新路径下可用。五、元数据目录示例PIP-157 给出了同一组主题在“无分桶”与“3 个桶”两种布局下的目录对照是该文档最直观的说明这里完整保留。原始层级无分桶managed-ledgers \- tenant \- namespace \- persistent - nptopic1 - nptopic2 - ptopic-partition-0 - ptopic-partition-1 - ptopic-partition-2 \- ptopic-partition-3在 3 个桶的情况下同样的主题元数据按如下方式分布managed-ledgers \- tenant \- namespace \- persistent - $0 | - ptopic-partition-0 | \- ptopic-partition-3 - $1 | - nptopic2 | \- ptopic-partition-1 \- $2 - nptopic1 \- ptopic-partition-2桶节点以$0、$1、$2命名各主题按“哈希绝对值对 3 取模”落入对应桶。注意示例中ptopic的 4 个分区被分散到了三个不同桶中——分桶按分区主题partitioned topic 的各个分区作为独立主题逐个计算而不是把整个逻辑主题固定在同一桶内。六、兼容性设计PIP-157 的兼容性承诺非常明确可以概括为两条存量命名空间不受影响既有命名空间以及创建时未显式启用该特性的命名空间行为与路径完全保持现状numberOfTopicBuckets缺省为 1 时即无桶层。启用了分桶的命名空间可以像其他命名空间一样使用管理、生产、消费、列举等所有既有 API 语义不变客户端无感知。这套“默认关闭 按命名空间启用 创建后不可变更桶数”的组合使得该特性天然向后兼容集群无需升级元数据格式旧路径与新路径可以长期共存于同一集群。七、被拒绝的替代方案PIP-157 的 Rejected alternatives 一节值得单独展开它解释了为什么最终选择了“按命名空间可选分桶”而不是其他看似更优雅的方案对所有命名空间全局引入分桶。这会让元数据结构更统一homogeneous但要求在所有 broker 升级完成后把全部主题的元数据从旧路径原子地搬迁到新路径需要复杂的更新逻辑与升级协调。允许修改桶数量。基于同样的原因变更桶数等价于一次全量元数据搬迁因此“可修改桶数”不在本提案的目标范围内。在 ZK 元数据存储实现内部解决。该问题虽然与 ZooKeeper 相关若把“分桶”逻辑塞进 ZK 版 metadata store 的实现就必须让该层理解“什么路径代表什么含义”即主题、命名空间从而破坏关注点分离——metadata store 应保持对路径语义无感知这正是提案坚持在 broker 侧命名空间策略 持久化命名计算解决的原因。八、结合当前仓库看特性接入点与现状把 PIP-157 的设计映射回当前仓库的代码结构可以清晰地看到几个既定的接入点持久化路径的统一出口TopicResources 是 broker 侧对/managed-ledgers的集中封装创建主题createPersistentTopicAsync使用topic.getPersistenceNamingEncoding()拼路径、存在性检查persistentTopicExists、列举listPersistentTopicsAsync、getExistingPartitions都经过这里。从源码结构看分桶后的列举逻辑逐桶 getChildren 再聚合以及桶化路径的创建/检查逻辑最自然的落点就是这个抽象层。命名翻译TopicName.getPersistenceNamingEncoding / fromPersistenceNamingEncoding 是domain://tenant/namespace/topic与持久化路径之间互译的唯一权威实现PIP 中“向该编码方法提供桶号”的描述正对应于此TopicNameTest 中的多组编码/解码断言也表明桶化改造需要以同样的测试风格固化新的四段/五段格式。策略载体numberOfTopicBuckets属于命名空间策略其不可变性约束需要在命名空间创建/策略修改的校验逻辑中落地。需要如实指出的是在当前仓库快照中代码检索未发现numberOfTopicBuckets策略或桶化路径的实现getPersistenceNamingEncoding也尚不接受桶号参数。因此本文关于分桶行为桶命名$N、哈希取模分配、逐桶列举开销等的描述均以 pip/pip-157.md 的提案文本为准属于“设计目标”层面的事实仓库源码部分则用于说明该设计在现有元数据链路上的挂载位置而非对已落地实现的描述。九、小结PIP-157 用一组非常克制的改动解决了 Pulsar 单命名空间主题数量的天花板问题机制在/managed-ledgers/tenant/namespace/domain/topic中插入桶层把大节点的 getChildren 拆分为若干个桶节点的小规模列举控制面新增命名空间策略numberOfTopicBuckets默认 1不分桶创建后不可修改桶与 bundle 无关联透明性客户端命名domain://tenant/namespace/topic不变分桶只发生在 broker 内部由getPersistenceNamingEncoding承载的持久化命名翻译中分配规则abs(topicName.hashCode()) % numberOfBuckets无状态、确定性兼容与代价存量命名空间零迁移列举主题的元数据请求数从 1 次变为“桶数”次schema 存储完全不动。对于运维大规模主题集群的读者而言该提案的核心价值在于它把“元数据扇出”从一个全局性的架构问题收敛为一个可按命名空间灰度开启、创建后冻结的局部策略是典型的以最小 API 面换取最大容量收益的设计。赞分享消息队列流处理后端微服务消息路由【免费下载链接】pulsarApache Pulsar - distributed pub-sub messaging system项目地址https://gitcode.com/gh_mirrors/pu/pulsar点击查看免费下载相关推荐Apache Pulsar 主题统计Topic Stats详解从命令行到源码级的指标解读Apache Pulsar 主题统计Topic Stats详解从命令行到源码级的指标解读 Apache Pulsar 提供了丰富的主题级topic le消息队列后端流处理trackerslist 选表指南5 个文件配好 qBittorrent 公共 Trackertrackerslist 选表指南5 个文件配好 qBittorrent 公共 Tracker 先说结论直接取 trackers_best.txt http消息队列流处理后端微服务消息路由Apache Pulsar 命名空间复制职责拆分PIP-321 与 allowed-clusters 机制深度解析Apache Pulsar 命名空间复制职责拆分PIP 321 与 allowed clusters 机制深度解析 PIP 321Split the res消息队列后端上一篇Zoom Classic OAuth Scopes 完整参考指南权限层级、资源类别与授权实践下一篇RisingWave 错误 UI 快照测试指南error_ui 目录的结构、原理与基线维护创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考