☰
Apache Pulsar 2.9.1 部署实战:从 standalone 到伪集群与压测
2026/10/14 23:55:22 网站建设 项目流程

简介:Apache Pulsar 2.9.1 二进制发行包面向分布式消息中间件开发者、云原生架构师及大数据实时计算从业者,用于快速搭建高吞吐、可扩展的发布/订阅消息系统。压缩包为 gz 格式,整体约 321.53MB,内含部署运行 Pulsar 所需的二进制文件、客户端库、启动脚本及辅助工具,解压后即可按官方文档配置并启动服务。Pulsar 依托 ZooKeeper 管理主题分区、集群配置与租约信息,实现高可用与一致性;借助 BookKeeper 提供低延迟持久化存储,并支持分层存储、Pulsar Functions 轻量流处理以及共享、独占、故障转移等多种订阅类型,可跨节点与数据中心部署,无缝集成 Kubernetes 等容器化环境。目前已有 151 人学习关注,适合需要构建大规模实时数据流、研究分布式协调与消息持久化机制的读者参考实践。

1. 拿到 apache pulsar 2.9.1 bin.tar.gz 之后:为什么你的第一反应不该是解压

很多人从下载页把 apache pulsar 2.9.1 bin.tar.gz 拖到服务器上,第一件事就是tar -zxvf然后bin/pulsar standalone,跑起来看到日志刷出messaging service is ready就以为完事了。我最早也这么干,直到有一次在测试环境用 standalone 模式压了半小时,重启后所有 topic 的 backlog 全丢,才意识到这个 tar 包里的默认配置压根不是给你上生产用的。这个压缩包本质上是 Pulsar 的「全量二进制发行版」,里面同时塞了 broker、bookie、proxy、functions worker 四套角色的启动脚本和默认配置,standalone 只是把它们揉在一个 JVM 里方便你尝鲜。它真正解决的问题是:让你在没有任何外部依赖(不装 ZooKeeper、不装 BookKeeper 集群)的前提下,五分钟内看到一条消息从 producer 进、从 consumer 出。适合谁?适合要做技术选型验证的后端、要写 Pulsar 客户端压测脚本的测试、以及需要本地复现某个消费位点问题的运维。但如果你打算把它当成生产部署的起点,那从解压那一刻起就得换一套思路——这个包是「原料」,不是「成品」。

2. 解压后的目录结构与四个角色的启动边界

2.1 bin 目录里哪些脚本能直接用,哪些是陷阱

解压完apache-pulsar-2.9.1之后,bin目录下脚本一大堆,新手最容易乱点。我一般只关注这几个:pulsar(总入口,带子命令)、pulsar-daemon(后台方式启动)、bookkeeper(单独起 bookie)、pulsar-admin(管理命令)。pulsar standalone和pulsar-daemon start standalone的区别在于前者前台阻塞、日志直接打屏,后者写logs/目录并 fork 到后台。验证阶段用前台,方便 Ctrl+C 干净退出;要挂着跑压测就用 daemon,但记得pulsar-daemon stop standalone收尾,否则残留的 bookie 进程会占着 3181 端口,下次启动直接报Address already in use。

# 解压到固定路径,避免路径里带空格或中文 tar -zxvf apache-pulsar-2.9.1-bin.tar.gz -C /opt cd /opt/apache-pulsar-2.9.1 # 前台启动 standalone,日志直接看屏,适合首次验证 bin/pulsar standalone # 另开一个终端,确认四类端口都在监听 # 6650 broker 对客户端,8080 HTTP/admin,3181 bookie,2181 内嵌 zk ss -lntp | grep -E '6650|8080|3181|2181'

这段命令的逻辑是:先固定安装路径,再前台拉起,最后用ss确认端口。参数上-C指定解压目标,-z解 gzip、-x解 tar、-v显示过程、-f指定文件,这五个字母顺序别记反,-f必须紧跟文件名。端口确认这一步很多人跳过,结果客户端连不上时反复改客户端配置,其实是 broker 根本没起来。6650是二进制协议端口,8080是 HTTP 管理和 prometheus 指标端口,3181是 bookie 的 journal 端口,2181是内嵌 ZooKeeper 的客户端端口——standalone 模式下这四个缺一不可。

2.2 conf 目录里 standalone 与生产配置的差异点

conf目录下standalone.conf和broker.conf是两套东西。standalone.conf里managedLedgerDefaultEnsembleSize=1、managedLedgerDefaultWriteQuorum=1、managedLedgerDefaultAckQuorum=1,三个 1 意味着每条消息只写一份、只等一个确认,性能好看但零冗余。生产用的broker.conf里这三个值通常是 3/3/2 或 2/2/2,取决于你的 bookie 节点数。还有一个隐蔽点:standalone.conf默认把bookkeeper的journalDirectory和ledgerDirectories都指向data/下的临时目录,重启后如果没做持久化挂载,数据就没了。我一般验证阶段会显式改data目录到一个独立磁盘路径,避免和系统盘抢 IO。

# conf/standalone.conf 里我通常会动的几行 # 把数据目录挪到独立盘,避免系统盘写满 dataDir=/data/pulsar/standalone # 单机验证可以保留 1/1/1,但要知道这是零冗余 managedLedgerDefaultEnsembleSize=1 managedLedgerDefaultWriteQuorum=1 managedLedgerDefaultAckQuorum=1 # 打开自动创建 topic,省得每次手动建 allowAutoTopicCreation=true

改完配置必须重启 standalone 才生效,pulsar-admin的brokers动态配置只能改一部分运行时参数,像dataDir这种启动期参数改不了。这里有个血泪经验:allowAutoTopicCreation在验证阶段开着方便,但生产环境一定要关,否则客户端拼错 topic 名会静默创建一个新 topic,消费端一直等不到消息,排查半天才发现是名字打错了。

2.3 用 pulsar-admin 验证 broker 是否真的可用

光看端口监听不够,得用管理命令实际打一次。pulsar-admin clusters list和pulsar-admin tenants list是最轻量的探活,如果这两个命令返回正常,说明 broker 的元数据层是通的。再进一步,手动建一个 topic 并往里面发一条消息,确认端到端链路。

# 探活:列出集群和租户,standalone 默认有 public 租户 bin/pulsar-admin clusters list bin/pulsar-admin tenants list # 手动建一个分区 topic,3 个分区 bin/pulsar-admin topics create-partitioned-topic \ persistent://public/default/verify-topic -p 3 # 用内置的 produce 子命令发一条消息 bin/pulsar-client produce \ persistent://public/default/verify-topic \ -m "hello-pulsar-291" -n 1 # 再用 consume 子命令读出来,-n 1 表示读一条就退出 bin/pulsar-client consume \ persistent://public/default/verify-topic \ -s verify-sub -n 1

create-partitioned-topic的-p 3指定分区数,分区数一旦创建不能改,这是 Pulsar 和 Kafka 都有的约束。pulsar-client produce的-m是消息内容,-n是发送条数;consume的-s是订阅名,-n是消费条数。如果 consume 卡住不返回,八成是订阅位点问题——-s verify-sub第一次用会从最早开始读,但如果之前用过同名订阅且已经消费过,就会从上次位点继续,这时要么换订阅名,要么加-p Earliest显式指定位点。这个坑我在联调时踩过,客户端代码里订阅名写死,重跑测试时死活读不到旧消息,最后发现是位点已经推到末尾了。

3. 从 standalone 到伪集群:把 bookie 和 broker 拆开跑

3.1 为什么要在单机上拆角色

standalone 把所有角色塞一个 JVM,方便但有个致命问题:你没法单独调 bookie 的 JVM 参数,也没法模拟 bookie 挂掉时 broker 的行为。做技术选型时,我强烈建议在单机上把 ZooKeeper、BookKeeper、Broker 拆成三个独立进程跑一遍,这叫「伪集群」。它能暴露很多 standalone 掩盖的问题,比如 bookie 磁盘写满后 broker 的降级逻辑、ZooKeeper 会话超时后 broker 的重连行为。拆开跑不需要多台机器,一台 8C16G 的机器足够,关键是端口别冲突。

3.2 伪集群的三份配置与启动顺序

启动顺序必须是 ZooKeeper → BookKeeper → Broker,因为 broker 启动时要连 ZK 拿元数据、连 bookie 写 ledger。Pulsar 包里自带了conf/zookeeper.conf、conf/bookkeeper.conf、conf/broker.conf,但默认值都是给集群用的,单机伪集群要改几个关键项。

# 第一步:起一个独立 ZooKeeper,用包里自带的配置改端口 # conf/zookeeper.conf 里确认 clientPort=2181,dataDir 指向独立目录 bin/pulsar-daemon start zookeeper # 第二步:起 bookie,改 conf/bookkeeper.conf # 关键:zkServers=127.0.0.1:2181,journalDirectory 和 ledgerDirectories 分开 bin/pulsar-daemon start bookie # 第三步:起 broker,改 conf/broker.conf # 关键:zookeeperServers=127.0.0.1:2181 # managedLedgerDefaultEnsembleSize=1(单 bookie 只能写一份) # configurationStoreServers=127.0.0.1:2181 bin/pulsar-daemon start broker

三份配置里最容易翻车的是bookkeeper.conf的journalDirectory和ledgerDirectories。journal 是写前日志,对磁盘 fsync 延迟极敏感,必须放在独立盘;ledger 是实际数据,可以放容量盘。单机伪集群如果只有一个盘,至少要把这两个目录分开到不同路径,避免 journal 的随机写和 ledger 的顺序写互相干扰。另一个坑是managedLedgerDefaultEnsembleSize,单 bookie 时如果设成 2 或 3,broker 会一直报Not enough bookies然后创建 topic 失败。这个报错信息很直白,但新手容易忽略,以为是 ZK 连不上。

3.3 用 prometheus 指标确认伪集群健康度

拆开跑之后,验证手段也要升级。standalone 看日志就够了,伪集群得看指标。Broker 的8080/metrics和 Bookie 的8000/metrics(bookie 默认指标端口)都能用 curl 拉。重点看几个:pulsar_broker_topics_count(topic 总数)、bookie_ledgers_count(ledger 数量)、bookie_journal_queue_size(journal 队列积压)。如果journal_queue_size持续上涨,说明 journal 盘 IO 跟不上了,这时候压测数据再好看也是假的。

# 拉 broker 指标,过滤出 topic 和订阅相关 curl -s http://127.0.0.1:8080/metrics | grep -E 'pulsar_broker_topics_count|pulsar_subscription_back_log' # 拉 bookie 指标,看 journal 队列和 ledger 数 curl -s http://127.0.0.1:8000/metrics | grep -E 'bookie_journal_queue_size|bookie_ledgers_count'

pulsar_subscription_back_log是订阅积压条数,这个指标在压测时比吞吐量更有参考价值——积压持续增长说明消费端跟不上,光看生产端 TPS 会误判。bookie_journal_queue_size是 journal 写队列长度,正常应该在个位数到几十之间波动,如果稳定在几百以上,说明 journal 盘该换了。这些指标在 standalone 模式下也能看,但拆开跑之后每个角色的指标独立,定位问题更准。

4. 避坑:apache pulsar 2.9.1 部署时最容易翻车的五件事

4.1 端口冲突导致 bookie 静默退出

现象:pulsar-daemon start bookie返回成功,但ss -lntp看不到 3181 端口,broker 日志报Connection refused连不上 bookie。原因:3181 被之前没杀干净的 bookie 进程占着,新进程启动时绑定失败但 daemon 脚本没把错误抛到前台。解决:启动前先ss -lntp | grep 3181确认端口空闲,有残留就pkill -f bookkeeper清掉,再启动。我一般会在启动脚本里加一句端口检查,省得每次手动看。

4.2 dataDir 指向临时目录导致重启丢数据

现象:standalone 重启后,之前创建的 topic 还在(元数据在 ZK),但消息全读不出来了,消费端报No such ledger。原因:standalone.conf默认dataDir在解压目录下,如果解压目录在/tmp或容器临时层,重启后数据被清。解决:把dataDir显式改到持久化路径,容器部署时挂 volume。这个坑在容器化环境里特别常见,很多人用官方镜像跑,没挂 volume,一重启就回到解放前。

4.3 客户端连接串写错协议前缀

现象:Java 客户端PulsarClient.builder().serviceUrl("pulsar://...")能连上,但换成http://就报Handshake failed。原因:6650 是二进制协议端口,必须用pulsar://前缀;8080 是 HTTP 端口,只给 admin 和 metrics 用。解决:客户端 serviceUrl 统一用pulsar://host:6650,管理命令用http://host:8080。这个错误在混合使用 admin API 和客户端 API 时容易犯,因为两者端口不同但都叫「服务地址」。

4.4 订阅位点重置的误操作

现象:想重新消费历史消息,用pulsar-admin topics reset-cursor重置位点,结果消费端还是从最新开始。原因:reset-cursor 需要指定订阅名和位点时间,如果订阅是Exclusive模式且消费者在线,重置可能不生效。解决:先停掉消费者,再执行reset-cursor -s 订阅名 -t 时间戳,确认返回cursor reset后再启动消费者。更稳妥的做法是删掉订阅重建,pulsar-admin topics unsubscribe然后重新订阅并指定-p Earliest。

4.5 JVM 堆内存默认值偏小

现象:压测到一定量级后 broker 频繁 Full GC,日志出现GC overhead limit exceeded,吞吐骤降。原因:bin/pulsar脚本里PULSAR_MEM默认值在 2.9.1 里偏保守,standalone 模式下 broker 和 bookie 共享一个堆,更容易触发。解决:启动前 exportPULSAR_MEM="-Xms4g -Xmx4g -XX:MaxDirectMemorySize=4g",直接内存也要给够,因为 Pulsar 大量用 Netty 的 direct buffer。这个参数在容器里还要注意和 cgroup 限制对齐,堆加直接内存别超过容器 limit,否则会被 OOM Killer 干掉。

5. 用压测脚本摸清 2.9.1 单机吞吐的真实上限

5.1 压测前必须固定的三个变量

在单机上压 Pulsar,如果不控制变量,测出来的数字毫无意义。我一般固定三个:消息大小(比如 1KB)、分区数(比如 6)、生产者和消费者线程数(比如各 3)。消息大小影响网络和磁盘 IO,分区数影响并行度,线程数影响客户端侧 CPU。这三个不固定,两次压测结果能差三倍。Pulsar 包里自带bin/pulsar-perf工具,不用自己写客户端就能压。

# 生产端压测:6 分区,3 线程,1KB 消息,持续 120 秒 bin/pulsar-perf produce \ persistent://public/default/bench-topic \ -r 50000 -s 1024 -t 3 -n 6 --duration 120 # 消费端压测:同样订阅,3 线程,确认消费速率 bin/pulsar-perf consume \ persistent://public/default/bench-topic \ -s bench-sub -t 3 --duration 120

-r是目标速率(条/秒),-s是消息字节数,-t是线程数,-n是分区数,--duration是持续秒数。注意-r是「尽力而为」的目标,实际速率受 broker 和磁盘限制,压测报告里会打出实际 TPS 和延迟分位数。消费端-s是订阅名,要和生产端配合——如果消费端启动晚于生产端,会从最早开始读(新订阅默认 Earliest),如果订阅已存在则从上次位点继续。

5.2 从压测报告里读三个关键数字

pulsar-perf结束后会打一份汇总,重点看三个:Throughput(实际 TPS)、p99 Latency(99 分位延迟)、Aggregated Latency(聚合延迟)。单机 1KB 消息、6 分区、3 线程的配置下,如果磁盘是普通 SSD,生产端 TPS 大概在几万到十几万之间,p99 延迟在几毫秒到几十毫秒。如果 p99 突然跳到几百毫秒,先看 bookie 的journal_queue_size,八成是 journal 盘 IO 打满了。消费端 TPS 如果明显低于生产端,看pulsar_subscription_back_log是否持续增长,增长说明消费能力不足,加消费者线程或分区。

5.3 一个容易被忽略的验证:重启后数据还在不在

压测跑完,别急着删 topic。pulsar-daemon stop broker再start broker,然后重新消费一遍,确认消息还能读出来。这一步验证的是 bookie 的持久化是否真的落盘。standalone 模式下如果dataDir没改,重启后大概率读不到;伪集群模式下如果 journal 和 ledger 目录配对了,重启后数据应该完整。我一般会在压测脚本最后加一段「重启验证」,虽然多花两分钟,但能避免上线后才发现持久化没生效的尴尬。

# 压测后重启 broker,再消费一次确认数据持久化 bin/pulsar-daemon stop broker sleep 5 bin/pulsar-daemon start broker sleep 10 # 用新订阅从最早读,-n 指定读多少条,确认能读到压测写入的数据 bin/pulsar-client consume \ persistent://public/default/bench-topic \ -s verify-after-restart -p Earliest -n 100

-p Earliest显式指定从最早位点读,避免新订阅默认行为的不确定性。-n 100读 100 条就退出,不用读完整个 topic。如果这一步报No such ledger或读不到数据,说明持久化配置有问题,回头检查bookkeeper.conf的ledgerDirectories和journalDirectory是否指向了持久化路径。这个验证习惯我保持了几年,帮我拦下过好几次「以为配好了其实没落盘」的部署。

5.4 把压测参数固化成可复用的脚本

每次手动敲pulsar-perf参数容易漏,我一般写一个bench.sh,把消息大小、分区数、线程数、持续时间做成变量,跑不同组合时只改变量。这样横向对比不同配置的吞吐时,不会因为手误改了某个参数导致数据不可比。脚本里还可以加一段自动拉指标的逻辑,压测结束后把pulsar_broker_topics_count和bookie_journal_queue_size打到日志里,方便回溯。

#!/bin/bash # bench.sh:固定变量,跑完自动拉指标 TOPIC="persistent://public/default/bench-$(date +%s)" RATE=50000 SIZE=1024 THREADS=3 PARTS=6 DURATION=120 bin/pulsar-admin topics create-partitioned-topic "$TOPIC" -p $PARTS bin/pulsar-perf produce "$TOPIC" \ -r $RATE -s $SIZE -t $THREADS -n $PARTS --duration $DURATION # 压测结束拉一次 broker 和 bookie 指标存档 curl -s http://127.0.0.1:8080/metrics | grep pulsar_broker_topics_count curl -s http://127.0.0.1:8000/metrics | grep bookie_journal_queue_size

用时间戳做 topic 名后缀,避免每次压测复用同一个 topic 导致位点混乱。create-partitioned-topic在 topic 已存在时会报错,但脚本里用时间戳基本不会重复。压测结束后指标存档这一步,我一般会把输出重定向到一个带日期的文件,方便后面做趋势对比。这套脚本不复杂,但能让你在换机器、换磁盘、换参数时快速得到可对比的数据,而不是每次凭感觉说「好像快了点」。

5.5 单机压测的边界:什么时候该上真集群

单机伪集群压出来的数字,只能作为「下限参考」,不能直接外推到生产。原因有三:单机的网络栈、磁盘 IO、CPU 都是共享的,broker 和 bookie 抢资源;单 bookie 没有副本,写成功不代表高可用;单 ZK 是单点,元数据层没有容错。我一般把单机压测的结论限定在「验证配置是否正确、客户端是否跑通、大致量级是否满足预期」这三件事上。真要评估生产容量,至少得三台 bookie、三台 ZK、两台 broker 起,而且压测客户端要独立于服务端机器,否则客户端和服务端抢 CPU,数字会失真。这个边界意识比压测本身更重要——见过太多人拿单机 standalone 的 TPS 去写容量规划,上线后被打脸。

希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询