Apache Pulsar 2.9.1 部署实战:单机、集群、多租户与避坑
简介:Apache Pulsar 2.9.1 的官方二进制发行包(bin.tar.gz),面向需要在分布式环境中搭建消息中间件的后端开发、数据平台与运维人员,用于应对高吞吐实时消息传递、集群容错与云原生部署等场景,适合已具备一定消息队列基础的中高级读者。压缩包约 321.53MB,解压后即可获得启动服务端与客户端所需的二进制文件、脚本及辅助工具,可按照官方文档完成配置与启动。内容围绕 Pulsar 的核心机制展开:依托 ZooKeeper 管理主题分区、集群配置与租约等元数据,借助 BookKeeper 实现低延迟、高可用的消息持久化,并支持独占、共享与故障转移等多种订阅类型;Pulsar Functions 提供轻量级流处理能力,分层存储可将冷数据迁移至低成本介质。已有 152 人学习,适合用来搭建本地实验集群、验证跨数据中心部署方案,或作为对比 Kafka 等中间件时的实操参考。
1. 从 bin.tar.gz 到一个能发消息的 Pulsar:2.9.1 包里到底装了哪几层
拿到 apache pulsar 2.9.1 bin.tar.gz 的人,多半已经在架构图上见过它:Broker 无状态、BookKeeper 存 Ledger、ZooKeeper 管元数据,计算存储分离,天生适合云原生环境里横向扩。可解压之后,conf 目录里同时躺着 zookeeper.conf、bookkeeper.conf、broker.conf、standalone.conf 四套配置,bin 目录下 pulsar、pulsar-admin、pulsar-client、pulsar-perf 又各有各的入口,先动哪个很容易发懵。这份二进制包的实际价值在于:不用编译、不用容器编排,一台装了 JDK 的机器就能把单机全栈拉起来,也能沿用同一套配置扩成三节点集群。它适合要验证消息模型选型、做吞吐压测、或者给内部跑一轮分布式消息 PoC 的从业者。下面按先跑通、再拆层、后治理、最后踩坑的顺序拆。
2. 解包与单机起步:standalone 五分钟跑通一条消息
standalone 模式是这份包里最容易被低估的东西。它并不是一个阉割版玩具,而是把 ZooKeeper、BookKeeper、Broker 三个角色塞进同一个 JVM 进程里,用同一套 data 目录落盘,topic 的创建、分区、订阅、Schema 全都能用。对刚接触 Pulsar 的人来说,先用它把 produce/consume 闭环跑通,比一上来就摆三台机器靠谱得多,因为集群模式下任何一个配置写错,日志都会含糊地指向 ZK 连接,排查成本高好几倍。
2.1 环境预检:JDK、内存与端口三件事
Pulsar 2.9.1 这一代对 JDK 比较挑。整套包在 JDK 8 和 JDK 11 上都能跑,但 JDK 17 会因为模块系统反射限制直接炸掉,报InaccessibleObjectException或者Unsupported class file major version 61——这类报错看起来像依赖缺失,实际是运行时版本不对。血泪经验是:先把java -version打出来确认是 11,再往下走。
内存方面,standalone 默认给 JVM 的堆是 2G 起步,BookKeeper 还要额外占堆外直接内存,机器少于 8G 时压测很容易触发 OOM Killer。文件句柄也不能忽视,Pulsar 每个 Ledger 都要占 fd,ulimit -n低于 65536 在几百个 topic 的场景下会开始报Too many open files。
# 1. 确认 JDK 版本,2.9.1 建议 11,8 也可 java -version # 2. 文件句柄与内存映射上限 ulimit -n sysctl vm.max_map_count # 3. 检查四个关键端口是否被占用 # 2181=ZooKeeper 3181=Bookie 6650=Broker 8080=WebService ss -lntp | grep -E '2181|3181|6650|8080'代码里的三段检查各自对应一个失败面:JDK 版本决定能不能启动,ulimit -n决定能撑多少 topic 和消费者,端口检查则是为了避免和机器上已有的 ZooKeeper、Kafka 之类服务撞车——2181 这个端口在很多大数据环境里已经有人占了,启动时只会看到Address already in use,不会告诉你用的是哪个进程。
vm.max_map_count在默认 65530 的发行版上一般够用,只有当 topic 数量上千、Ledger 频繁打开关闭时才需要调到 20 万以上。
2.2 standalone 启动与必须改的三个配置
解包之后不要急着敲启动命令。conf/standalone.conf里有几个值在单机场景下必须改,否则后面客户端连不上会很莫名其妙。
tar -xzf apache-pulsar-2.9.1-bin.tar.gz cd apache-pulsar-2.9.1 # 关键三项:对外通告地址、数据目录、Web 端口 sed -i 's/^advertisedAddress=.*/advertisedAddress=192.168.10.21/' conf/standalone.conf sed -i 's#^dataDir=.*#dataDir=/data/pulsar/standalone#' conf/standalone.conf sed -i 's/^webServicePort=.*/webServicePort=8080/' conf/standalone.conf # 前台启动,日志直接打屏,第一次建议这样跑 bin/pulsar standaloneadvertisedAddress是这三个参数里最容易被忽略、后果也最严重的一个。它的作用不是绑定监听地址,而是告诉客户端"你应该用这个地址回来找我"。默认值取的是机器 hostname,如果 hostname 在客户端那台机器上解析不了,或者解析到一个不可达的内网地址,表现就是:命令行pulsar-client produce能连上 6650 发出去,但消费端一直TimeoutException,broker 日志里干干净净。我第一次踩这个坑时盯了半天 broker 日志,最后发现客户端拿到的是pulsar://myhost:6650,而 myhost 在测试机上根本没配 hosts。
dataDir改成独立磁盘目录是为了两件事:一是默认的data/在安装目录里,重装升级时容易被顺手删掉;二是生产环境上 Ledger 写入对磁盘 IO 敏感,放系统盘会和日志抢 IO。standalone 模式下 ZooKeeper 的 snapshot、BookKeeper 的 journal 和 ledgers 都会落到这个目录下,一次压测就能写掉几十个 G。
启动成功时,日志里会出现messaging service is ready,随后自动创建public/default这个默认租户和命名空间。这一步不需要手动建,但要知道它存在,因为后面所有不带前缀的 topic 名都会落到这里。
2.3 用 pulsar-client 走完 produce/consume 闭环
包里自带的pulsar-client是个轻量命令行客户端,够用来做冒烟验证。整个闭环分三步:发消息、起订阅消费、看统计。
# 1. 往默认命名空间发 5 条消息 bin/pulsar-client produce persistent://public/default/smoke-test \ --messages "hello-pulsar" -n 5 # 2. 起一个订阅,从最早位置开始消费 5 条 bin/pulsar-client consume persistent://public/default/smoke-test \ -s smoke-sub -n 5 -p Earliest # 3. 看这个 topic 的元数据与吞吐 bin/pulsar-admin topics stats persistent://public/default/smoke-test第一条命令里-n 5是发送条数,--messages后面跟的是消息体,多条消息用逗号分隔。第二条命令的-s指定订阅名,-p Earliest表示从最早的消息开始读——这个参数如果不加,默认是Latest,在消息已经发完之后再起消费者是读不到任何东西的,新手最容易在这里以为"消息丢了"。-n 5是消费够 5 条就退出,不写会一直挂着。
第三条命令返回的 JSON 值得逐字段看一遍,尤其是msgRateIn、msgThroughputIn、storageSize和subscriptions下的msgBacklog。msgBacklog是积压量,消费端卡住时这个数字会一直涨,比看日志直观得多。
顺序上有个反直觉的点:先发消息后起订阅,在Earliest模式下没问题;但如果订阅是Exclusive且已经存在,第二次用同一个订阅名起消费者会直接报ConsumerBusyException。这不是 bug,是 Pulsar 的订阅语义在起作用,验证时换个订阅名就行。
3. 从单机到集群:ZooKeeper、BookKeeper、Broker 三层怎么分工
standalone 跑顺之后,绝大多数人的下一个动作是"我要上集群"。Pulsar 2.9.1 的集群拓扑是固定的三段式,中间没有简化余地。理解这三层各自存什么,比背启动命令重要得多,因为集群模式下的报错信息几乎全部指向"某层连不上另一层",不知道分工就只能盲猜。
3.1 2.9.1 为什么还离不开 ZooKeeper
Pulsar 在 2.9 这一代用了两套元数据存储:本地元数据存储(local metadata store)和配置存储(configuration store),默认两者都落在 ZooKeeper 上。前者管集群内的动态状态——哪些 broker 活着、topic 归属哪个 broker、订阅游标在哪;后者管集群级别的配置——租户、命名空间、分区数、保留策略这些。
BookKeeper 自己也依赖 ZooKeeper 来存 Ledger 的元数据,也就是"某个 Ledger 被哪些 bookie 持有"这份映射。所以 2.9.1 的集群里,ZooKeeper 是被两套系统共用的,这一点在部署时经常被误判成"Pulsar 的 ZK 和 BookKeeper 的 ZK 是两拨",结果配了两套集群,白多花资源。
真正让 ZK 变成架构约束的是它的写路径:所有元数据变更都要走 ZK 的 ZAB 协议,节点数越多写越慢。这也是后续版本把 ZK 换成可插拔元数据接口、最终在 3.0 移除 ZK 的直接原因。用 2.9.1 就得接受这个前提:ZK 三节点起步,且必须放在低延迟网络上,跨机房部署 ZK 是自找麻烦。云原生场景下更常见的做法是把 ZK 放同一可用区,Broker 层再横向扩,把弹性压在无状态的那一层。
3.2 三节点集群的配置清单与启动顺序
下面这套是三节点最小可用拓扑:三台机器各跑一个 ZK、一个 bookie、一个 broker。角色混布是为了省机器,代价是单机故障影响面大,PoC 阶段够用,生产环境建议拆开。
先改 ZK 配置,三台机器的conf/zookeeper.conf核心只有一段:
# conf/zookeeper.conf,三台机器内容一致 dataDir=/data/pulsar/zookeeper clientPort=2181 server.1=zk1:2888:3888 server.2=zk2:2888:3888 server.3=zk3:2888:3888server.N里的 N 必须和该机器dataDir/myid文件里的数字一致,2888 是 ZK 节点间数据同步端口,3888 是选举端口。这两个端口写错不会报错,只会表现为集群一直选不出 leader,日志反复刷LOOKING。
BookKeeper 侧改conf/bookkeeper.conf:
# conf/bookkeeper.conf zkServers=zk1:2181,zk2:2181,zk3:2181 advertisedAddress=bookie1 bookiePort=3181 journalDirectory=/data/pulsar/journal ledgerDirectories=/data/pulsar/ledgersjournalDirectory和ledgerDirectories分开放是 BookKeeper 的硬性建议:journal 是顺序写、延迟敏感,ledgers 是随机读、容量敏感,混在一块盘上互相拖累。advertisedAddress和前面 standalone 的道理一样,写 hostname 就要保证集群内所有机器都能解析。
Broker 侧改conf/broker.conf:
# conf/broker.conf zookeeperServers=zk1:2181,zk2:2181,zk3:2181 configurationStoreServers=zk1:2181,zk2:2181,zk3:2181 clusterName=pulsar-cluster advertisedAddress=broker1 bookkeeperMetadataServiceUri=zk+hierarchical://zk1:2181;zk2:2181;zk3:2181/ledgers webServicePort=8080 brokerServicePort=6650bookkeeperMetadataServiceUri这一行是 2.9 版本里最容易写错的地方。注意它用的是分号分隔、前缀是zk+hierarchical://,和上面两行的逗号分隔格式完全不同。写成逗号会导致 broker 启动时报 URI 解析失败,而这个报错在日志里埋得很深。
启动顺序是固定的:ZK 全部起来并确认有 leader → 初始化集群元数据 → bookie 起来 → broker 起来。初始化那条命令只需在整个集群生命周期里执行一次:
# 只在第一台 broker 上执行一次,注册集群元数据 bin/pulsar initialize-cluster-metadata \ --cluster pulsar-cluster \ --zookeeper zk1:2181 \ --configuration-store zk1:2181 \ --web-service-url http://broker1:8080 \ --broker-service-url pulsar://broker1:6650 # 三台机器上分别按顺序起进程 bin/pulsar-daemon start zookeeper bin/pulsar-daemon start bookkeeper bin/pulsar-daemon start broker--web-service-url和--broker-service-url填的是客户端将来访问的入口,可以指向 VIP 或负载均衡地址,不一定非要是某台 broker 的真实 IP。这两个值写进去之后就存在 ZK 里了,后面用bin/pulsar-admin clusters update改,改错了客户端会连到不存在的地址。
验证集群是否成形,用三条命令逐层看:
bin/pulsar-admin clusters list bin/pulsar-admin brokers list pulsar-cluster bin/pulsar-admin broker-stats brokers第一条返回集群名,第二条返回 broker 列表,第三条能看到每个 broker 的 topic 数和吞吐。如果第二条返回空,说明 broker 没注册上,问题一定在zookeeperServers或clusterName上;如果 broker 在列表里但客户端连不上,问题在advertisedAddress。
3.3 ensembleSize / writeQuorum / ackQuorum 怎么定
这三个参数决定了消息写入的持久化强度和延迟,是集群调优里最先要定的东西。它们的值写在broker.conf里,作用于新创建的 Ledger。
| 参数 | 含义 | 3 bookie 推荐值 | 5 bookie 推荐值 | 影响 |
|---|---|---|---|---|
managedLedgerDefaultEnsembleSize | 一条 Ledger 横跨多少个 bookie | 3 | 5 | 决定数据分布宽度,越大越难写满 |
managedLedgerDefaultWriteQuorum | 每条消息写几份副本 | 3 | 3 | 决定冗余度,等于 ensemble 时最安全 |
managedLedgerDefaultAckQuorum | 收到几份确认才算写成功 | 2 | 2 | 决定写延迟,越小越快但越不安全 |
配置片段:
# conf/broker.conf,追加在文件末尾 managedLedgerDefaultEnsembleSize=3 managedLedgerDefaultWriteQuorum=3 managedLedgerDefaultAckQuorum=2约束关系是ackQuorum <= writeQuorum <= ensembleSize。三副本写三确认二,意味着任意一台 bookie 挂掉不影响写入,两台同时挂才会拒绝写。这个组合在延迟和可靠性之间比较平衡,是多数 PoC 的起点。
把ackQuorum降到 1 能让写延迟明显下降,代价是单副本确认就算成功,bookie 进程崩溃时可能丢最近的一批消息。反过来把三者都设成 5,写延迟会翻倍,因为每次写要等五个 bookie 都落盘。我一般会在压测里把这两组值各跑一遍,拿 P99 延迟数据去说服人,比讲理论有用。
需要注意的是,这三个参数只影响之后新建的 Ledger。已经存在的 topic 要改,得用bin/pulsar-admin topics set-persistence单独设置,改配置文件对存量 topic 无效——这条边界踩过一次就记住了。
4. 多租户与 Topic 治理:tenant / namespace / topic 三级结构实操
Pulsar 和多数消息队列最大的结构差异就在这三级命名空间上。Kafka 只有 topic 一层,多业务共用集群时靠命名约定区分;Pulsar 把租户和命名空间做成了一等公民,配额、权限、保留策略全部挂在这一层。用得好,一个集群能安全地服务多个业务线;用不好,就是给每个业务建一个public/default下的 topic,白白浪费了这套设计。
4.1 三级命名空间与命名规范
完整 topic 名的格式是persistent://tenant/namespace/topic,2.9 里还有non-persistent://前缀用于不需要落盘的消息。租户(tenant)是隔离的最高层,命名空间(namespace)是策略的作用域,topic 是实际的消息通道。
落地时我习惯这么映射:租户对应业务线或团队,命名空间对应环境(prod、staging),topic 名用「实体.事件」的格式,比如order.created、payment.refunded。这样从 topic 名就能反推出归属和用途,排查时不用翻文档。
# 建租户,--admin-roles 指定谁能管理这个租户 bin/pulsar-admin tenants create app-a --admin-roles ops-team # 建命名空间,生产环境一个、预发一个 bin/pulsar-admin namespaces create app-a/prod bin/pulsar-admin namespaces create app-a/staging # 看命名空间下有哪些 topic bin/pulsar-admin topics list app-a/prod--admin-roles需要配合认证插件才有实际效果。2.9.1 默认是不开认证的,生产环境要么在前面挡一层网关,要么开authenticationEnabled=true配 JWT 或 mTLS,这部分配置量不小,PoC 阶段可以先跳过。
4.2 用 pulsar-admin 设定保留与配额策略
命名空间这一层的价值全在策略上。默认策略对生产环境基本不适用:消息既没有保留上限,也没有积压上限,一个卡住的消费者能把磁盘吃满。
# 保留策略:最多留 10G 或 7 天,先到先删 bin/pulsar-admin namespaces set-retention app-a/prod \ --size 10G --time 7d # 积压配额:积压超过 20G 时拒绝生产者写入 bin/pulsar-admin namespaces set-backlog-quota app-a/prod \ --limit 20G --policy producer_request_hold # 建一个 6 分区的 topic bin/pulsar-admin topics create-partitioned-topic \ persistent://app-a/prod/order.created --partitions 6 # 查看生效的策略 bin/pulsar-admin namespaces get-retention app-a/prod bin/pulsar-admin namespaces get-backlog-quota app-a/prod--size和--time是「或」的关系,任一条件满足就删。只设时间不设大小,遇上突发流量磁盘会先满;只设大小不设时间,低频 topic 的旧数据会一直占着。
--policy有三个取值:producer_request_hold是拒绝新消息,producer_exception是直接抛异常给客户端,consumer_backlog_eviction是丢弃最老的消息。生产环境选前两个,让上游感知到背压;consumer_backlog_eviction会静默丢数据,除非业务能容忍,否则别用。
分区数一旦定下来,2.9.1 只能增不能减,扩容用bin/pulsar-admin topics update-partitioned-topic。分区数往上加之后,Key_Shared 订阅的 key 分布会重新洗牌,如果有顺序依赖要提前规划好。
4.3 订阅模式选型:四种语义的适用边界
订阅模式选错了,表现往往不是报错,而是消息乱序或者重复消费,排查起来很费劲。四种模式的差别在于「同一个订阅下允许多少消费者、消息怎么分发」。
| 模式 | 同订阅消费者数 | 消息分发 | 顺序保证 | 典型场景 |
|---|---|---|---|---|
| Exclusive | 1 | 全部给唯一消费者 | 全局有序 | 单消费者串行处理、状态机 |
| Failover | 多(主备) | 只发给主消费者 | 全局有序 | 需要高可用又要求顺序 |
| Shared | 多 | 轮询分发 | 不保证 | 无状态并行消费、吞吐优先 |
| Key_Shared | 多 | 按 key 哈希分发 | 同 key 有序 | 按业务主键保序的并行消费 |
Key_Shared 是这四种里最有实用价值也最容易配错的。它保证同一个 key 的消息落到同一个消费者,从而在并行消费的同时保住业务主键级别的顺序。但它要求生产端显式设置 key,不设 key 的消息会被随机分发;另外 2.9 版本的 Key_Shared 在消费者扩缩容时会有短暂的 key 重分配,那段时间内同一个 key 可能被两个消费者各处理一次,幂等逻辑不能省。
用命令行验证订阅模式时,直接指定类型即可:
# Shared 模式起两个消费者,观察消息被分摊 bin/pulsar-client consume persistent://app-a/prod/order.created \ -s shared-sub -t Shared -n 20 -p Earliest-t后面跟订阅类型,-s是订阅名。同一个订阅名起两个进程,Shared 模式下两边会各拿到一部分;换成 Exclusive,第二个进程直接报错退出。这个对比实验五分钟就能做完,比看文档印象深。
5. 避坑与排查:2.9.1 二进制包最容易翻车的五个地方
这一章是纯经验账。下面五条都是我在不同环境里真实撞过的,按「现象 → 原因 → 解决」写,能省掉的排查时间按小时算。
5.1 启动阶段卡住的三条
现象一:bin/pulsar standalone启动后进程存活,但日志反复刷KeeperException: ConnectionLoss,Web 端口 8080 打不开。
原因:advertisedAddress填了主机名,而本机 hosts 里没有对应记录,ZooKeeper 客户端解析失败后又不断重试。standalone 模式下 ZK 和 broker 在同一进程里,这个自连接失败会拖住整个启动流程。
解决:/etc/hosts里补一条127.0.0.1 your-hostname,或者把advertisedAddress直接改成 IP。集群模式下则要保证所有节点互相能解析。
现象二:启动脚本执行后秒退,日志里有UnsupportedClassVersionError或InaccessibleObjectException。
原因:机器上默认 JDK 是 17 或更高,2.9.1 的依赖链里有反射调用老 API 的代码,模块系统一拦就崩。
解决:装一个 JDK 11,在conf/pulsar_env.sh里显式指定JAVA_HOME,不要依赖 PATH。顺手在脚本里加一行打印java -version的检查,以后再换机器不容易翻车。
现象三:bookie 启动报No writable ledger dir,进程直接退出。
原因:ledgerDirectories指向的目录不存在,或者存在但属主不是启动用户。还有一个变体是磁盘写满,BookKeeper 检测到剩余空间低于阈值会拒绝启动。
解决:mkdir -p建目录并chown给启动用户,df -h确认剩余空间。BookKeeper 默认要求磁盘至少留 5% 余量,调这个阈值用diskSpaceThreshold。
5.2 运行阶段翻车的两条
现象四:生产者能连上并发送成功,消费者一直收不到消息,pulsar-admin topics stats里msgBacklog持续增长。
原因:两个可能。一是订阅位置用了Latest,消费者起来时消息已经发完;二是客户端从 broker 拿到的是advertisedAddress,而那个地址在客户端侧不可达,于是消费者反复重连、永远建不起消费流。
解决:先确认订阅位置,用-p Earliest重试;再在客户端机器上telnet advertisedAddress 6650验证连通性。broker 侧的advertisedAddress配错是最常见的一类,改完要重启 broker 才生效。
现象五:跑压测一段时间后 broker 日志刷Too many open files,新连接建不上,已有消费延迟飙升。
原因:ulimit -n是会话级的,bin/pulsar-daemon启动的进程继承的是当前 shell 的限制。机器上配了 65536,但启动脚本在另一个 shell 里跑的,实际生效的可能是 1024。
解决:在conf/pulsar_env.sh里显式ulimit -n 65536,重启进程,用cat /proc/<pid>/limits确认生效。这条坑的隐蔽之处在于它要跑一段时间才暴露,压测十分钟看不出来。
6. 把 2.9.1 用出长期价值:分层存储、压测口径与版本边界
6.1 分层存储:让老消息离开 SSD
2.9.1 自带 offloaders 目录,里面已经躺好了 aws-s3、gcs、azure、filesystem 几种驱动的 nar 包,不用自己编。配置写在conf/broker.conf:
# conf/broker.conf,以 S3 兼容存储为例 managedLedgerOffloadDriver=aws-s3 managedLedgerOffloadMaxThreads=2 s3ManagedLedgerOffloadBucket=pulsar-offload s3ManagedLedgerOffloadRegion=us-east-1 s3ManagedLedgerOffloadServiceEndpoint=http://storage.internal:9000 offloadersDirectory=./offloaders配置要重启 broker 才生效,然后用阈值触发:
bin/pulsar-admin namespaces set-offload-threshold --size 5G app-a/prod--size 5G的意思是某个 topic 的 Ledger 累计超过 5G 就自动把最老的段搬到对象存储,broker 本地只留索引。搬运是异步的,对在线读写影响很小。要注意s3ManagedLedgerOffloadServiceEndpoint这个参数,用自建的对象存储时必须填,而且不要带桶名路径。
6.2 压测口径:别只看 QPS
pulsar-perf是包里自带的压测工具,用法和 pulsar-client 类似但参数更细:
bin/pulsar-perf produce persistent://app-a/prod/order.created \ -r 5000 -s 512 -t 8 -n 500000 bin/pulsar-perf consume persistent://app-a/prod/order.created \ -s perf-sub -r 5000 -t 8-r是目标速率(条/秒),-s是消息体字节数,-t是线程数,-n是总条数。压测结果里我会固定看四个数:发布 P99 延迟、消费 P99 延迟、msgBacklog是否归零、以及 broker 的 CPU 和网卡是否打满。单看 QPS 数字很容易被误导——限速 5000 跑出来正好 5000,说明瓶颈根本不在服务端。
6.3 停留在 2.9.1 的边界
2.9.1 是 2021 年前后的版本线,之后社区继续往 2.10、2.11、3.x 走,元数据层从 ZK 解耦、最终在 3.0 移除 ZK 是明确方向。这意味着 2.9.1 适合做 PoC、做选型验证、跑内部压测,但不适合当作长期演进的基线;如果业务已经确定要上,升级路径要提前画出来,尤其是自定义的认证插件和 Functions 代码,跨大版本迁移时改动量不小。
从那以后我每次起 Pulsar 环境,都强制走一遍「java -version→ulimit -n→advertisedAddress解析验证 →pulsar-admin brokers list」这四步,不管多急都跑完再往下做,这四步能挡掉我遇到的八成翻车场景。希望帮到你。
本文还有配套的精品资源,点击获取