简介:Apache Pulsar 2.9.1 二进制发行包面向需要构建高吞吐、可扩展消息中间件的后端开发与运维人员,尤其适合正在评估或落地分布式发布/订阅架构、准备在云原生环境中部署消息队列的团队。压缩包约 321.53MB,内含部署与运行 Pulsar 所需的二进制文件、启动脚本、客户端库及辅助工具,解压后即可按官方文档配置服务。Pulsar 以 BookKeeper 提供低延迟持久化存储,支持共享、独占与故障转移等多种订阅类型,并借助 ZooKeeper 管理主题分区、集群配置与租约信息,从而保障分布式环境下的一致性与高可用;其分层存储与 Pulsar Functions 轻量流处理能力,也便于控制成本并在消息链路中直接完成数据转换。资源已有 152 人学习,适合希望快速搭建本地或集群实验环境、理解 Pulsar 与 ZooKeeper 协作机制的读者参考使用。
1. 从一台内存不够的测试机说起:apache pulsar 2.9.1 单机环境怎么落地
上周帮同事在一台 8GB 内存的测试机上装 apache pulsar 2.9.1,解压完直接bin/pulsar standalone,三分钟不到进程就被系统 OOM 掉了,日志里只剩一行含糊的Killed。这不是 Pulsar 本身有问题,而是很多人把「单机跑通」和「生产部署」混为一谈,默认参数直接怼上去,BookKeeper 的写缓存和堆内存一起把机器吃干净。apache pulsar 2.9.1 这个版本在云原生场景里出镜率很高,配合 apache zookeeper 做元数据协调是它的标准形态,但分布式组件凑在一起,版本、端口、内存、JDK 任何一项没对齐,装完就是起不来或者起来就挂。这篇记录面向的是想在本机或测试环境把 2.9.1 跑通、并且能验证消息真的收发成功的开发者,从版本选择、依赖准备、配置改动到排错,按我实际装过一遍的顺序写下来。
2. 装前把环境列清楚:JDK、内存与端口三件事
2.1 版本组合与 JDK 选择
apache pulsar 2.9.1 对 JDK 的要求比较明确:官方构建和运行基线是 JDK 8 和 JDK 11,JDK 17 在这个版本上并没有被完整验证过,尤其是 BookKeeper 和 Netty 的一些反射调用在 17 上会触发模块访问限制。我一般会选 JDK 11,原因是 8 虽然稳,但 2.9.x 的部分依赖已经按 11 编译,用 11 能少踩一类UnsupportedClassVersionError。装之前先确认版本:
java -version # 期望输出类似: # openjdk version "11.0.20" 2023-07-18 # OpenJDK Runtime Environment (build 11.0.20+8)如果机器上同时有多个 JDK,用update-alternatives --config java切过去,别指望JAVA_HOME设了就行——Pulsar 的启动脚本会优先读JAVA_HOME,但某些发行版的pulsar脚本里还会回退到which java,两者不一致时会出现「命令行 java 是 11,Pulsar 却用 8 跑」的玄学问题。稳妥做法是JAVA_HOME和PATH里的 java 指向同一个目录,启动前用echo $JAVA_HOME再核一遍。
内存这块要给个明确数字:单机 standalone 模式,官方建议至少 4GB 空闲内存,但实际跑起来 BookKeeper + ZooKeeper + Broker 三合一,8GB 机器上如果不调堆参数,很容易被 OOM Killer 盯上。我的经验是测试机给 Pulsar 进程留 4GB 堆、系统留 2GB 以上,低于这个配置就别开 standalone,改用容器分组件跑。
2.2 端口占用与 ZooKeeper 的角色
2.9.1 的 standalone 会内嵌一个 ZooKeeper 实例,默认端口 2181,同时 Broker 占 6650(二进制协议)和 8080(HTTP 管理),BookKeeper 占 3181。装之前先扫一遍端口:
ss -lntp | grep -E '2181|6650|8080|3181' # 有输出说明被占用,需要先停掉对应进程或改配置这里要澄清一个常见误解:standalone 模式内嵌的 ZooKeeper 只用于本地元数据,和你单独部署的 apache zookeeper 集群不是一回事。如果你打算后续切到分布式模式,元数据存储要指向外部 ZooKeeper,那时候conf/standalone.conf里的zookeeperServers和configurationStoreServers就得改成外部地址。单机阶段先别动,保持内嵌,减少变量。
2.3 目录规划与文件准备
下载下来的apache-pulsar-2.9.1-bin.tar.gz解压后是一个完整目录,我习惯放到/opt/pulsar下,数据目录单独挂一块盘或者单独建目录,避免日志和数据混在一起把根分区写满:
sudo mkdir -p /opt/pulsar /data/pulsar sudo tar -xzf apache-pulsar-2.9.1-bin.tar.gz -C /opt/pulsar --strip-components=1 ls /opt/pulsar # 应看到 bin conf lib examples 等目录--strip-components=1是为了去掉压缩包里的顶层目录名,让文件直接落在/opt/pulsar下,后面所有脚本路径都以这个为基准。数据目录/data/pulsar先建好,配置阶段再指过去。这一步看着简单,但解压到带空格或者中文的路径下,启动脚本里的相对路径解析会出问题,血泪经验是路径只用英文和短横线。
3. 解压后的第一轮配置:standalone.conf 里改哪几个值
3.1 内存参数:别让默认值把机器拖垮
conf/standalone.conf是单机模式的入口配置,2.9.1 里它同时管 Broker 和 BookKeeper 的堆设置。默认的PULSAR_MEM给得比较保守,但在小内存机器上仍然偏大,我一般显式改成固定值:
# 编辑 conf/standalone.conf,找到或新增以下行 PULSAR_MEM="-Xms2g -Xmx2g -XX:MaxDirectMemorySize=2g"-Xms和-Xmx设成一样是为了避免运行期堆伸缩带来的抖动;MaxDirectMemorySize控制 Netty 的堆外内存,BookKeeper 写条目走的是直接内存,这个值给小了会报OutOfDirectMemoryError,给大了又和堆抢物理内存。2GB 堆 + 2GB 直接内存是 8GB 测试机上比较稳的组合,机器更小就等比往下压,但堆别低于 1GB,否则 Broker 启动时加载元数据就会卡。
改完用grep PULSAR_MEM conf/standalone.conf确认没有重复定义——有些版本模板里这行是注释掉的,你新增一行而旧行没删,脚本取最后一个,容易改了个寂寞。
3.2 数据与日志路径:把写入引到独立目录
默认数据落在data/下,日志落在logs/下,都在安装目录里。生产习惯是把它们挪出去:
# conf/standalone.conf 中调整 dataDir=/data/pulsar/data # 日志目录通过环境变量控制,启动前 export export PULSAR_LOG_DIR=/data/pulsar/logsdataDir管的是 BookKeeper 的 ledger 和 ZooKeeper 的快照,写入量随消息量线性增长,放根分区迟早写满。日志目录用环境变量是因为 2.9.1 的pulsar脚本读PULSAR_LOG_DIR,写进配置文件反而不生效。这两个路径改完要确保运行用户有写权限,chown -R一遍再启动,权限不足时报错信息往往只提示「无法创建文件」,不告诉你具体哪个目录。
3.3 关闭不需要的组件与端口收敛
单机测试环境不需要对外暴露管理端口时,可以把webServicePort改成非默认值,减少和本机其他服务的冲突:
# conf/standalone.conf webServicePort=8088 brokerServicePort=6658改端口后所有客户端连接串都要跟着变,pulsar-client默认连 6650,测试时记得显式指定--url pulsar://localhost:6658。这一步不是必须的,但如果机器上已经跑了别的 Web 服务占着 8080,不改就会启动失败,报Address already in use,排查时先看端口再看配置,顺序别反。
4. 启动与验证:从进程起来到消息真的收发成功
4.1 启动命令与日志观察
配置改完,用前台方式先起一次,方便看日志:
cd /opt/pulsar bin/pulsar standalone # 前台运行,Ctrl+C 退出;确认无误后再改后台启动过程会依次拉起 ZooKeeper、BookKeeper、Broker,日志里出现messaging service is ready才算真正就绪,这中间大概 20 到 40 秒。如果卡在Starting bookkeeper超过一分钟,多半是dataDir权限或者磁盘空间问题;如果报ZooKeeper session timeout,检查 2181 端口有没有被别的进程抢了。后台运行用bin/pulsar-daemon start standalone,停止用bin/pulsar-daemon stop standalone,别直接kill -9,BookKeeper 的 ledger 没正常关闭,下次启动要做恢复,时间会明显变长。
4.2 用内置客户端验证生产消费
进程起来不等于能用,必须走一遍真实收发。2.9.1 自带pulsar-client工具:
# 终端一:消费,先起消费者避免消息漏看 bin/pulsar-client consume my-topic -s sub1 -n 10 # 终端二:生产 10 条消息 bin/pulsar-client produce my-topic -m "hello-pulsar-1,hello-pulsar-2" -n 10consume的-s是订阅名,-n是消费条数,消费够数自动退出;produce的-m是消息内容,多条用逗号分隔,-n是发送条数。两边都正常时,消费者终端会打印出消息体和 message id。如果生产者报Failed to create producer,先看 Broker 日志里有没有Topic not found之外的权限或命名空间错误;如果消费者一直挂着没输出,检查订阅名是否和生产者所在的命名空间匹配,standalone 默认租户是public/default,topic 名不带前缀时会自动落到这个命名空间。
4.3 管理接口自检
除了客户端,HTTP 管理接口也能快速判断 Broker 状态:
curl -s http://localhost:8088/admin/v2/clusters # 正常返回 ["standalone"] curl -s http://localhost:8088/admin/v2/tenants # 正常返回 ["public"]这两个接口能通,说明 Broker 的 Web 服务和元数据存储都正常。如果第一个通第二个 404,多半是初始化没完成,等几秒重试;如果都连不上,回到进程和端口层面查,别在配置里绕。
5. 装坏了怎么救:五条踩坑记录
5.1 启动即被 Killed,日志无异常
现象:执行bin/pulsar standalone后进程几秒内消失,logs/下没有明显 ERROR,dmesg里能看到Out of memory: Killed process。
原因:PULSAR_MEM和MaxDirectMemorySize之和超过物理内存,或者机器上还有其他 Java 进程占着内存,OOM Killer 直接动手。
解决:按 3.1 节把堆和直接内存压到物理内存的一半以内,启动前free -h确认可用内存,必要时先停掉其他 Java 服务。改完用前台方式起,观察 RSS 增长是否平稳。
5.2 端口冲突导致启动中断
现象:日志报BindException: Address already in use,进程退出。
原因:2181、6650、8080、3181 中某个被占用,常见是本机已有 ZooKeeper 或另一个 Pulsar 实例没停干净。
解决:ss -lntp | grep <端口>找到占用进程,能停就停,不能停就按 3.3 节改webServicePort和brokerServicePort,ZooKeeper 端口在conf/standalone.conf的zookeeperPort里改。改完记得同步客户端连接串。
5.3 JDK 版本不匹配的隐蔽报错
现象:启动时报UnsupportedClassVersionError或NoSuchMethodError,指向 Netty 或 BookKeeper 的类。
原因:JAVA_HOME指向 JDK 8,但PATH里的 java 是 17,或者反过来,脚本和手动命令用了不同 JDK。
解决:echo $JAVA_HOME和which java对齐到同一个 JDK 11 目录,java -version再确认一次。多版本机器上用update-alternatives统一,别只改环境变量。
5.4 数据目录权限不足导致 BookKeeper 起不来
现象:日志停在Starting bookkeeper,随后报Permission denied或Unable to create directory。
原因:dataDir指向的目录属主不是运行用户,或者磁盘只读、空间不足。
解决:chown -R <运行用户> /data/pulsar,df -h确认空间,mount | grep <挂载点>确认不是只读挂载。改完重启,别在权限没修好时反复重启,容易留下半初始化状态。
5.5 消费端收不到消息的订阅名问题
现象:生产者发送成功,消费者终端无输出也不报错。
原因:消费者用的订阅名和之前测试残留的订阅状态冲突,或者 topic 名带了命名空间前缀而生产者没带,两边实际不是同一个 topic。
解决:换一个全新的订阅名重试,bin/pulsar-admin topics list public/default确认 topic 真实存在,生产消费两端统一用完整 topic 名persistent://public/default/my-topic。standalone 模式下残留订阅不会自动清理,测试时勤换订阅名能省不少排查时间。
6. 装完怎么算跑通:一套五分钟自检流程
装完别急着交付,我固定走一遍自检,五分钟内能确认这套 2.9.1 是真能用还是假活。第一步看进程和端口,ps -ef | grep pulsar确认只有一个 standalone 主进程,ss -lntp确认 2181、6650、8088、3181 都在监听;第二步打管理接口,/admin/v2/clusters和/admin/v2/tenants都要返回预期 JSON;第三步走一次完整收发,生产 10 条、消费 10 条,条数对得上才算数;第四步看日志尾部,tail -n 50 logs/pulsar-standalone.log里不应有 ERROR 级别的持续刷屏,偶发 WARN 可以接受;第五步重启一次,bin/pulsar-daemon stop standalone再start,确认数据目录里的 topic 还在,消费能接着之前的位点走。
| 检查项 | 命令 | 通过标准 |
|---|---|---|
| 进程 | ps -ef | grep standalone | 仅一个主进程,无残留子进程 |
| 端口 | ss -lntp | grep -E '2181|6650|8088|3181' | 四个端口均监听 |
| 管理接口 | curl -s localhost:8088/admin/v2/clusters | 返回["standalone"] |
| 收发 | pulsar-client produce/consume | 消费条数与生产一致 |
| 重启保持 | pulsar-daemon stop/start | topic 与订阅状态保留 |
这套流程里最容易糊弄过去的是第三步,有人只看生产者返回成功就认为通了,实际上消息可能落在 ledger 里但消费者因为订阅问题读不到。我吃过一次亏:生产端一切正常,消费端静默,查了半天才发现是 topic 名少了persistent://前缀,两端指向了不同实体。从那以后我每次装完 Pulsar,都强制用完整 topic 名走一遍生产消费,再重启验证一次状态保留,两步都过才敢说这套环境跑通了。希望帮到你。
本文还有配套的精品资源,点击获取