
简介Apache Pulsar 2.9.1 二进制发行包面向需要构建高吞吐、可扩展消息中间件的后端开发与运维人员尤其适合正在评估或落地分布式发布/订阅架构、准备在云原生环境中部署消息队列的团队。压缩包约 321.53MB内含部署与运行 Pulsar 所需的二进制文件、客户端库、启动脚本及辅助工具解压后即可按官方文档配置服务。Pulsar 依托 ZooKeeper 管理主题分区、集群配置与租约等元数据保障分布式环境下的一致性与高可用其消息模型支持共享、独占与故障转移等多种订阅类型持久化层由 BookKeeper 提供低延迟、高可靠的日志存储并支持分层存储与 Pulsar Functions 轻量流处理。目前已有 152 人学习下载适合希望系统了解 Pulsar 架构、搭建本地或集群实验环境并验证消息收发与容错能力的读者参考。1. 拿到 apache pulsar 2.9.1 bin.tar.gz 先别急着解压它到底能解决什么一个做实时风控的团队把 Kafka 换成了 Pulsar理由不是吞吐量而是多租户隔离和存算分离——同一套集群里风控、埋点、告警三条业务线各自建 namespace互不干扰扩容时只加 bookie 不动 broker。这个场景恰好是 apache pulsar 2.9.1 bin.tar.gz 这类二进制包最典型的落地方式解压即用单机三进程ZooKeeper、BookKeeper、Broker跑起来就能验证生产级架构。它解决的是「想快速摸清 Pulsar 分层存储与订阅模型又不想从源码编译」的诉求适合后端工程师、中间件运维和正在做消息队列选型的架构人员。下面按我实际拆包的顺序讲从目录结构到单机启动、多租户配置、再到踩过的坑全部可复现。2. 解压后先看目录bin 包里到底装了什么2.1 目录结构与三个核心进程的关系tar 包解开后是一个apache-pulsar-2.9.1目录真正需要关心的只有四块bin/放启动脚本conf/放全部配置lib/放依赖 jarexamples/放官方示例。Pulsar 的架构决定了它启动时不是单进程而是三层ZooKeeper 负责元数据协调租户、namespace、topic 的归属BookKeeper 负责消息持久化bookie 存 ledgerBroker 负责对外协议和路由。bin 包里这三个角色都有独立启动脚本单机模式下它们跑在同一台机器上生产环境则拆开部署。先确认包完整性和 Java 环境这一步翻车的人不少# 校验包完整性有 sha512 文件时 sha512sum -c apache-pulsar-2.9.1-bin.tar.gz.sha512 # 解压 tar -xzf apache-pulsar-2.9.1-bin.tar.gz cd apache-pulsar-2.9.1 # 确认 Java 版本2.9.x 要求 JDK 8 或 11 java -versionsha512sum -c读的是官方附带的校验文件能挡住下载中断导致的半包java -version必须落在 8 或 11JDK 17 在 2.9.1 上会因模块化限制在 BookKeeper 启动阶段报反射错误这是版本边界不是配置能绕过的。解压后ls bin/能看到pulsar、pulsar-daemon、bookkeeper、pulsar-zookeeper等脚本pulsar是前台启动pulsar-daemon是后台启动调试阶段一律用前者日志直接打屏省得去翻logs/。2.2 单机三进程启动顺序与验证单机模式最省事的入口是bin/pulsar standalone它把 ZooKeeper、BookKeeper、Broker 一次性拉起来但生产习惯是先分开起再验证这样出问题能定位到具体进程。分开启动的顺序不能乱ZooKeeper 先起BookKeeper 依赖它注册元数据Broker 最后起并连接前两者。# 1. 启动 ZooKeeper前台另开终端 bin/pulsar-daemon start zookeeper # 2. 启动 BookKeeper bin/pulsar-daemon start bookkeeper # 3. 启动 Broker bin/pulsar-daemon start broker # 验证三进程 jps | grep -E QuorumPeerMain|Bookie|PulsarBrokerpulsar-daemon start会把进程放后台并把 pid 写到data/下jps里QuorumPeerMain是 ZooKeeper 的 JVM 主类Bookie和PulsarBroker分别对应另外两个。三个都出现才算启动成功缺一个就去看logs/pulsar-*.log。常见情况是 BookKeeper 起不来但 ZooKeeper 正常日志里报No ledger directories configured那是conf/bookkeeper.conf里ledgerDirectories指向的目录没建手动mkdir -p data/bookkeeper/ledgers即可。启动完成后用bin/pulsar-admin brokers list确认 broker 已注册返回集群名和 broker 地址就说明元数据链路通了。3. 多租户与 namespace把「apache zookeeper 分布式」落到配置里3.1 租户、namespace、topic 三级模型怎么建Pulsar 的资源模型是三层tenant租户→ namespace命名空间→ topic。多租户隔离靠的就是这层结构不同业务线各占一个 tenant配额、权限、保留策略都挂在 namespace 上。建之前先想清楚命名tenant 用业务域namespace 用环境加用途topic 再细分。下面这套命令是单机验证多租户的标准动作# 建租户带 admin 角色 bin/pulsar-admin tenants create risk-control \ --admin-roles risk-admin --allowed-clusters standalone # 建 namespace设置消息保留 7 天 bin/pulsar-admin namespaces create risk-control/realtime bin/pulsar-admin namespaces set-retention risk-control/realtime \ --time 7d --size 10G # 建分区 topic3 个分区 bin/pulsar-admin topics create-partitioned-topic \ persistent://risk-control/realtime/events --partitions 3--allowed-clusters standalone是关键参数单机集群名默认就是standalone不写或写错会导致 tenant 建了但 namespace 创建时报Cluster not found。set-retention的--time和--size是「或」关系任一条件满足就触发删除10G 是单 namespace 上限按磁盘余量给。create-partitioned-topic的--partitions 3决定并行消费上限分区数后期只能增不能减起步按消费者线程数的 1.5 倍给比较稳。3.2 用 pulsar-client 验证生产消费链路配置建完必须用客户端跑通一次生产消费否则等于没验证。bin 包里自带pulsar-client命令行工具不用写代码就能测# 生产一条消息 bin/pulsar-client produce \ persistent://risk-control/realtime/events \ --messages user_1001,amount500,risklow -n 1 # 消费从头读读 1 条退出 bin/pulsar-client consume \ persistent://risk-control/realtime/events \ -s verify-sub -n 1 --subscription-position Earliest-s verify-sub是订阅名Pulsar 的消费位点挂在订阅上而不是消费者上同一个订阅名换客户端能接着上次位点读这是它和 Kafka 消费组语义的差异点。--subscription-position Earliest表示新订阅从最早消息开始默认是Latest调试时忘了加会以为消息丢了。生产端-n 1是发一条消费端-n 1是收一条就退出适合脚本化验证。如果消费端卡住不返回先查 topic 是否有消息pulsar-admin topics stats再查订阅是否已存在且位点已到末尾。4. 避坑排查单机跑 Pulsar 最容易翻车的五处4.1 现象standalone 启动后 broker 立刻退出原因多半是端口冲突。Pulsar 默认占用 2181ZooKeeper、3181BookKeeper、6650Broker 二进制协议、8080admin HTTP。本机已有 ZooKeeper 或别的服务占了 2181ZooKeeper 起不来broker 连不上就退出。解决ss -lntp | grep -E 2181|6650|8080查占用改conf/zookeeper.conf的clientPort和conf/broker.conf的brokerServicePort、webServicePort三处端口要同步改漏一处照样起不来。4.2 现象消费端报Topic not found原因不是 topic 没建而是自动创建被关了。2.9.1 默认allowAutoTopicCreationtrue但如果你在broker.conf里手动设成 false或者 namespace 上设了--allowed-clusters不含当前集群客户端连上来就找不到 topic。解决先pulsar-admin topics list risk-control/realtime确认存在不存在就显式create-partitioned-topic存在则查pulsar-admin namespaces get-anti-affinity-group之类策略是否误配。血泪经验是别依赖自动创建生产一律显式建 topic。4.3 现象BookKeeper 写入慢日志刷LedgerClosedException原因是 ledger 目录所在磁盘 IO 打满或者conf/bookkeeper.conf里journalDirectories和ledgerDirectories放在了同一块盘。BookKeeper 的 journal 是顺序写、ledger 是随机读同盘会互相拖累。解决分盘挂载journal 放 SSDledger 放容量盘单机验证至少把两个目录分开到不同路径改完重启 bookie。这个坑在压测阶段才暴露单条消息测不出来。4.4 现象pulsar-admin命令报HTTP 401原因是开了认证但没带 token。2.9.1 默认authenticationEnabledfalse一旦在broker.conf打开所有 admin 命令都要--auth-params或环境变量。解决验证阶段先保持关闭要开就配AuthenticationProviderToken生成 token 后用bin/pulsar-admin --auth-params token:xxx ...。别在单机验证时开认证纯属给自己加戏。4.5 现象JVM 内存吃满被 OOM Killer 干掉原因是conf/pulsar_env.sh里PULSAR_MEM默认给得偏大单机三进程叠加超过物理内存。解决按机器内存分配ZooKeeper 给 512mBookKeeper 给 1gBroker 给 1g改PULSAR_MEM-Xms512m -Xmx512m -XX:MaxDirectMemorySize256m三个 conf 下的 env 脚本分别调。MaxDirectMemorySize别省Pulsar 大量用堆外内存做网络缓冲给太小会报Direct buffer memory。5. 进阶用分区 topic 和订阅类型压出真实行为单机验证跑通后真正值得花时间的是把订阅类型和分区消费的行为摸清楚这决定了你后面选型时的判断。Pulsar 有四种订阅Exclusive、Failover、Shared、Key_Shared。Shared 是多个消费者分摊分区Key_Shared 保证同一 key 落到同一消费者——风控场景按用户 ID 做 key同一用户的事件必须有序就得用 Key_Shared。下面这段用 Java 客户端演示 Key_Shared 的关键配置// 关键片段Key_Shared 订阅 按 userId 做 key Consumerbyte[] consumer client.newConsumer() .topic(persistent://risk-control/realtime/events) .subscriptionName(risk-key-shared) .subscriptionType(SubscriptionType.Key_Shared) .messageListener((c, msg) - { // 同一 userId 的消息只会进同一个消费者实例 System.out.println(new String(msg.getKeyBytes()) - new String(msg.getValue())); c.acknowledgeAsync(msg); }) .subscribe(); Producerbyte[] producer client.newProducer() .topic(persistent://risk-control/realtime/events) .create(); producer.newMessage().key(user_1001).value(amount500.getBytes()).send();subscriptionType(Key_Shared)是核心配合生产端key(...)才能生效acknowledgeAsync用异步确认避免逐条阻塞。注意 Key_Shared 在 2.9.1 上要求消费者数不超过分区数超出的消费者会空转这是版本限制。验证方法起两个消费者发 100 条带不同 key 的消息观察每个 key 是否只出现在一个消费者的输出里。我一般还会用pulsar-admin topics stats看msgRateIn和msgThroughputIn是否与预期一致数字对不上先查分区数而不是查网络。从那以后我每次换消息队列做 PoC都强制先跑一遍「分区 Key_Shared 位点续读」三件套再谈吞吐。希望帮到你。本文还有配套的精品资源点击获取