☰
COSCon‘25 Pulsar专场回顾:存算分离、消息轨迹与STM32环境监测实战
2026/9/30 4:04:04 网站建设 项目流程

COSCon‘25 的大幕拉开时,我在会场入口看到那块写着“Make MQ Great Again”的立牌,旁边摆着一排 Pulsar 的周边,说实话心里是有点五味杂陈的。消息队列这个领域已经很久没有这种“主场感”了,过去几年大家聊 MQ,十有八九聊到最后都变成 Kafka 的部署调优,好像消息中间件的可能性已经被讲完了。但这次 COSCon’25 x Pulsar Developer Day 2025 的专场,从上午主会场到下午动手实验区,全程都保持着极高的讨论密度。这篇文章我想以参会者视角做个回顾,聊聊专场里最值得记住的技术话题,也把我在现场看到的一些生产实践、IoT 场景实验和社区讨论记录下来。无论你是做后端中间件选型,还是在搞 STM32 环境监测这类设备接入,应该都能从中找到一些可以落地的思路。

1. 开场:为什么今年的主题喊出了“Make MQ Great Again”

1.1 一个口号背后的行业情绪

“Make MQ Great Again”这个口号挂在会场里,乍一听有点玩梗的味道,但真正在现场待一天就会发现,这其实是在回应一种普遍焦虑:消息队列技术是不是已经进入平台期了?很长一段时间里,大家的默认选项就是 Kafka,提到 MQ 就是分区、副本、消费者组,提到性能就是吞吐量数字。但 Pulsar 专场从第一场分享开始,就在努力打破这种惯性认知。

我对这个口号的理解是:MQ 不是没有新东西可讲,而是太久没有人系统性地讲这些新东西了。Pulsar 的存算分离、多租户模型、分层存储、跨地域复制,这些特性单拎出来每一个都能解决实实在在的问题,但过去在社区里始终没有一个足够集中的场合把它们揉在一起讲清楚。COSCon’25 把 Pulsar Developer Day 2025 放进同一个会场,相当于给这些分散的话题提供了一个集中的出口。现场分享的既有 Apache Pulsar 的 PMC 成员,也有在一线维护大规模集群的工程师,还有从 Kafka 迁到 Pulsar 之后回来做复盘的用户。

这就不只是一场“布道式”的技术分享,更像是一次把底牌亮出来的同行交流。我在现场听到最多的一个观点是:不要为了换而换,但如果你已经被 Kafka 的分区扩容、rebalance 抖动、存储成本这些问题反复折磨,Pulsar 值得认真看一眼。

1.2 COSCon 与 Pulsar Developer Day 同场的原因

COSCon 是开源年会,Pulsar Developer Day 是围绕 Apache Pulsar 生态的开发者活动,两个放在一起并不是简单的场地共享。从议程设置上能看出来,Pulsar 专场的很多内容都紧扣开源协作这条主线:有讲如何参与社区贡献的,有讲内部实现如何反哺上游的,也有讲开源项目在生产环境大规模落地经验的。

这种组合对参会者的价值在于:你不仅能听到“这个功能怎么用”,还能听到“这个功能是怎么被设计出来、为什么被设计成这个样子”。比如现场有人直接问 PMC 成员,为什么 Pulsar 的 topic 模型要塞进 tenant 和 namespace 两层,为什么不直接拍平。答案是多租户隔离从来不只是权限问题,还关系到配额管理、存储隔离和跨团队成本核算。这种问题如果只看文档,很容易被忽略。

所以这篇回顾我不打算写成流水账,而是把专场里对实际工作有直接帮助的几个话题挑出来展开,再补充一些我在现场记录到的细节和自己的延伸思考。

2. 主会场最值得回味的三个技术话题

2.1 存算分离到底解决了什么问题

上午主会场的第一个高密度话题就是存算分离。这个概念的宣传很多,但现场主讲人没有停留在“Broker 无状态、存储走 BookKeeper”这种一句话解释上,而是用一个对比把核心痛点讲透了。

Kafka 的 Broker 既要负责计算,又要负责存储分区数据,扩缩容的时候数据要在节点之间搬来搬去,分区数一多、副本数一高,rebalance 就容易成为稳定性事故的高发区。Pulsar 把这两层拆开了:Broker 只负责消息的路由、权限、元数据管理等计算逻辑,真正的消息数据落在 BookKeeper 节点上。这样一来,计算资源不够就加 Broker,存储不够就加 BookKeeper 节点,两边完全独立扩缩容,数据搬移被降到了最低。

我自己的理解可以打个比方:Kafka 像开了一家餐厅,每个服务员同时兼任仓库保管员,客人一多要加服务员,就得把仓库里的货也一起搬过去分一遍;Pulsar 则是服务员只管点菜上菜,所有菜品统一放在中央厨房,哪个餐厅忙不过来就派更多服务员去,厨房不够用就单独扩厨房。这个类比在现场交流时得到了不少人的认同。

BookKeeper 的写入机制其实也值得细说。它把一个 Topic 的数据切成若干 ledger 段,每条消息写入时,BookKeeper 会在一个 ensemble 里选择若干节点,按 write quorum 写入副本,并按 ack quorum 等待确认。简单理解,就是同一份数据同时写多个节点,只要法定数量的副本确认成功,就可以向客户端返回成功,这样单个节点故障不会丢数据,也不需要像 Kafka 那样依赖分区 leader 做同步复制。现场有人问“这个设计会不会让写入延迟变高”,讲师给出的实测数据是,在大部分内网场景下,RTT 增加在毫秒级,相比它换来的运维弹性是值得的。

2.2 消息轨迹:排查线上问题最容易被忽略的能力

第二个在会场引发激烈讨论的话题是消息轨迹。做消息中间件的人都有过这种经历:消费者明明消费到了消息,但业务结果不对;或者消息确实发送成功了,但一直卡在某个环节,谁也说不清卡在哪。这时候如果中间件只能告诉你“消息在”,但给不了全链路的流转记录,排查就只能靠日志靠猜。

Pulsar 的消息轨迹功能,本质上是在消息从 producer 到 broker 再到 consumer 的整个流转路径上,把关键事件记录下来,包括进入时间、存储位置、投递次数、确认时间等。现场演示的场景是一个订单系统,模拟了一条消息被消费者处理失败后进入重试队列的全过程。打开消息轨迹查询界面,就能清楚看到这条消息在哪个时间点被消费了两次,第一次没确认、第二次被成功消费并确认,顺着这个线索很快就定位到了业务代码里的幂等逻辑缺陷。

这个功能在 Kafka 生态里相对要费劲一些,通常需要自己埋点或者靠外部系统去审计;Pulsar 把它做成了内置能力,而且在 OpenTelemetry 的整合上也已经比较成熟,可以把消息轨迹导出到 APM 系统做统一观测。我做中间件运维这几年的体会是,消息轨迹这种东西平时用不上,一旦用到就是救命的,建议所有上了消息队列的项目都从一开始就把它打开。

2.3 分层存储与容量规划的账要怎么算

分层存储是另一个大家问得最多的话题。Pulsar 的分层存储可以把 BookKeeper 里比较老的数据自动卸载到 S3、GCS 或者自建的对象存储上,Broker 仍然能看到这些数据,消费者需要消费老消息时再把它加载回来,但存储成本可以大幅下降。

现场讲师算了一笔很直观的账:假设每天消息增量 1 TB,保留 7 天,BookKeeper 里大约需要 7 TB(副本 x3 就是 21 TB);如果打开分层存储,把超过 1 天的数据卸载到对象存储,BookKeeper 只需要承载 1 天的热数据,剩下的 6 TB 放到对象存储里。同样是 3 副本,热数据用 SSD 按 GB 计费,冷数据用对象存储按 TB 计费,成本相差可能接近一个数量级。

容量规划的现实问题在于,很多团队的 Topic 数量是持续增长的,消息量也是持续增长的,但存储预算不会跟着线性涨。分层存储并不能解决所有问题,它解决的是“历史数据还要不要留、留多久”的取舍问题。我建议在做容量规划时别只盯存储总量,更要关注消费滞后时间——如果消费者已经落后好几个小时,说明要么消费端能力不足,要么 Topic 的 key 设计导致热点严重,这些问题不是加存储能解决的。

3. 动手实验区:STM32 环境监测与 MQTT 接入的那些事

3.1 摆满开发板的桌子:DHT11、BH1750、MQ-2 和 OLED 的组合

下午的动手实验区有一张桌子特别热闹:一排 STM32 开发板,上面插着 DHT11 温湿度传感器、BH1750 光照传感器、MQ-2 气体传感器,还挂着一块 0.96 寸的 OLED 屏。第一眼看到这个组合,很多做后端的人可能觉得走错片场了,但恰恰是这套环境监测系统,把“Make MQ Great Again”里“MQ”的另一层含义串起来了——MQ-2 是测气体的传感器,MQ 是消息队列,两者在这个场景里构成了完整的数据链路。

这套硬件的分工清晰得很:STM32 负责采集和逻辑控制,DHT11 通过单总线协议返回温度和湿度,BH1750 通过 I2C 返回光照强度,MQ-2 输出的是模拟电压信号,要用 STM32 的 ADC 读取再换算成气体浓度,OLED 屏负责把实时数据直接显示出来。现场实验的目标很直接:让这堆传感器数据不仅能看,还能通过网络传出去,最终落到消息队列里。

我在旁边看了一会儿,发现大部分人来动手区之前都以为难的是硬件接线,实际跑起来才发现,真正让现场一片“翻车”声的反而是数据采集之后的网络上报环节。传感器采集是确定性的本地操作,串口一开、数据一读,结果就出来了;但一旦涉及网络上传、协议转换、消息队列的 topic 设计,各种问题就开始冒头。

3.2 传感器数据如何真正进入消息队列

先从传感器读取说起。DHT11 是典型的单总线器件,时序要求很严格,我整理了一下实验现场最稳定的读取流程:

  • STM32 的 GPIO 先拉低 18 ms 触发启动信号
  • 然后拉高并释放总线,等待 DHT11 响应
  • 设备会在总线上输出 40 bit 的数据:16 bit 湿度、16 bit 温度、8 bit 校验

以下是现场示例的关键代码,用的是标准库方式:

// STM32F103 读取 DHT11 的核心流程 GPIO_Mode_Input(GPIOB, GPIO_Pin_11); DHT11_Start(); // 拉低18ms后释放 DHT11_WaitResponse(); // 等待80us低电平 + 80us高电平 uint8_t humi = DHT11_ReadByte(); // 湿度整数部分 uint8_t temp = DHT11_ReadByte(); // 温度整数部分 DHT11_ReadByte(); // 跳过小数部分(DHT11小数位常为0) uint8_t checksum = DHT11_ReadByte();

BH1750 走的是标准 I2C,STM32 硬件 I2C 直接读就行,注意首次上电后需要发送一次 Power On 命令,否则读回来的全是 0。MQ-2 更简单,传感器模块输出模拟量,接到 STM32 的 ADC 引脚,用公式把 ADC 值换算成电压,再映射到气体浓度的相对值。OLED 用 I2C 接口,所有数据汇总之后直接刷到屏幕上,就能在本地先确认采集无误。

接下来就是数据上报。现场实验链路是:STM32 通过 WiFi 模块连上局域网,用 MQTT 协议发布消息到本地 Broker,再由 Broker 通过 Pulsar 的 MQTT Protocol Handler 直接把消息桥接到 Pulsar 集群。这样做的原因很简单:Pulsar 的核心定位是服务端消息中间件,设备端协议太重;而 MQTT 是为物联网设计的轻量协议,天然适合传感器这种低功耗、低带宽的场景。

如果把 Pulsar 的 MQTT 接入打开,设备侧其实可以不做任何代理,直接用 MQTT 客户端连接 Pulsar 的 Broker 端口,认证之后往指定 topic 发消息就行。现场演示用的是这种最直接的方式:STM32 发布到persistent://iot/device/temperature,消费端直接用 Python 的 Pulsar 客户端订阅,一个基于环境监测的完整消息链路就跑通了。

import pulsar client = pulsar.Client("pulsar://192.168.1.100:6650") consumer = client.subscribe( "persistent://iot/device/temperature", subscription_name="stm32-sub", consumer_type=pulsar.ConsumerType.Shared ) while True: msg = consumer.receive() data = msg.data().decode() print(f"收到传感器数据: {data}") consumer.acknowledge(msg)

这条链路跑通的瞬间,实验区里有一种“原来如此”的氛围。很多人搞完了才反应过来:消息队列的价值恰恰体现在这种跨协议、跨层级的整合上——设备端不用关心数据最终去哪,队列层也不用关心设备长什么样。

3.3 现场最常见的三个翻车点

实验区翻车最多的三个问题,我特意记了下来,给以后自己做环境监测系统的人提个醒。

第一个是 DHT11 读不到数据。排查下来发现八成是引脚接触不良或者时序里缺少上拉电阻。DHT11 的数据线需要外接一个 4.7kΩ 左右的上拉电阻,不然高电平信号容易被干扰。现场有几个板子直接靠模块自带上拉才没事,一旦你单独买裸的 DHT11 元件自己接,忘了上拉就是大概率翻车。

第二个是 MQ-2 初始读数直接爆表。这不是代码问题,MQ-2 通电之后需要预热,传感器内部有一根加热丝,刚上电的那几十秒输出电压会虚高。现场很多人接好之后立刻测试,ADC 读回来的数值直接拉满,就以为是模块坏了,其实只要等大概一分钟再读就正常了。

第三个是消息丢失和重复消费。有一组实验设置了 QoS 0 上报,偶尔把 WiFi 一断再连,消息就丢了;另一组用了 QoS 2,现场离线和重连之后消息倒是没丢,但消费端出现了重复,原因是没有做去重。这正好引出下面这个话题:在真实 IoT 场景里,可靠性和代价之间怎么权衡。

4. 从传感器到队列:IoT 场景下消息选型的真实考量

4.1 数据量小也要用消息队列吗

这是动手区讨论最热烈的问题,没有之一。一个 STM32 环境监测系统,每秒或者每几秒才上报一条温湿度数据,这种量级用 HTTP 直接 POST 到后端也完全够用,为什么还要中间塞一个消息队列?

我当时的回答是:数据量小不代表不需要消息队列,关键在于你是否需要削峰填谷、可靠投递和多下游消费。单机实验确实用不上,但一旦设备数量从 1 变成 1000,采集频率从每秒一次变成每秒十次,网络抖动和下游处理瓶颈就会陆续出现。

消息队列在 IoT 场景里最大的价值,类比过来就像一个快递中转站。设备是一条条快递线路上的发货点,后端服务是最终收件人。如果没有中转站,快递员必须亲手把每个包裹交到收件人手里,收件人一忙,快递就得排队;有了中转站,发货点只管按规则把包裹送到站里,收件人什么时候有空什么时候来取。削峰填谷、解耦、流量控制,就这么一次性解决了。

另外还有一个经常被忽略的审计价值。传感器数据进了消息队列,天然就有留存、有消费记录、有消息轨迹,将来要排查“昨天某段时间某个设备的数据为什么没进库”,至少能查得到。用 HTTP 直连,数据到了服务端之后如果处理失败,可能连影子都找不着。

4.2 MQTT Broker 与 Pulsar 的角色划分

实验区用 Pulsar 原生 MQTT 接入很顺畅,但真实生产环境里,我更倾向于把 MQTT Broker 和 Pulsar 划分成两层。

角色协议核心职责典型部署
设备接入层MQTT 3.1.1 / 5.0设备鉴权、连接保持、QoS、遗嘱消息EMQX / Mosquitto,靠近设备侧
消息服务层Pulsar持久化、转发、多租户隔离、流处理、投递给业务后端Pulsar 集群,靠近数据中心

分工的理由在于:设备侧连接数量大、长连接多、断线重连频繁,MQTT Broker 对这一类场景做了大量优化,比如连接保活、会话恢复、遗嘱消息;而 Pulsar 的核心优势是服务端的吞吐、持久化、多租户和流式处理生态。设备接入层负责把海量连接管好,Pulsar 负责把数据可靠地分发给下游,各干各擅长的事情。

而且这种分层还有一个操作上的好处:将来设备侧协议升级,比如从 MQTT 换到 CoAP 或者私有协议,只需要动接入层,Pulsar 的 Topic 和消费端完全不用变。反过来,业务消费方想从流式处理改成批量分析,也只需要在 Pulsar 这边增加一个消费组或者开启 Pulsar IO,设备侧完全无感。

4.3 端侧到云端的时延与可靠性实测

实验区现场对端到端时延做了简单测试:STM32 发布消息到 Pulsar,Python 客户端收到消息,中间过了 MQTT 协议转换,整个链路在局域网环境下基本在 20-100 毫秒之间。这个延迟对环境监测来说绰绰有余,现场甚至有人开始讨论能不能用这套链路做设备控制,那就需要引入另一个话题:QoS 的取舍。

MQTT 接入层通常有三种 QoS:

  • QoS 0:至多一次,可能丢消息,传感器周期性上报时可用
  • QoS 1:至少一次,保证消息到达,但可能重复,接收方要幂等
  • QoS 2:恰好一次,消息不丢不重,但开销最大,适合控制指令

我自己的想法是,环境监测这类周期性上报数据用 QoS 1 就够了,重复几条没关系,反正下一轮数据马上又来,消费端做好按设备 ID 和时间戳的去重就可以;控制类指令建议上 QoS 2,但需要后端从 Pulsar 里消费数据后做二次确认,确保指令只执行一次。现场有人问“Pulsar 本身有没有恰好一次语义”,答案是 Pulsar 支持事务消息,可以配合消息轨迹和消费端幂等设计,实现端到端的可靠处理,但要把它用好,需要从消息键到消费逻辑全链路一起规划,不是开个开关就能搞定的事。

5. 从硬件到生产:我印象最深的现场瞬间与给后来者的实在建议

5.1 一个让我印象深刻的现场问答

动手区接近尾声的时候,有一个做嵌入式开发背景的参会者问了个问题,当时全场安静了一下。他问的是:STM32 这种资源受限的设备,为什么要对接 Pulsar 这种重量级的服务端消息中间件,是不是有点杀鸡用牛刀?

这个问题的潜台词其实很多人都有,只不过没直接表达出来。现场做后端的人给的回答很实在:设备端永远只是整个系统的一环,单看一个 STM32 节点,数据量确实小,但如果你是一个做智慧农业或者智慧楼宇的项目,几百个节点的数据都要汇聚、存储、分析,消息队列的价值就会显现。

另外,Pulsar 可以选择性压缩消息,gzip、zstd、lz4、snappy 都支持。传感器上报的数据都是 JSON 文本,压缩比例通常能达到 70% 以上,存储成本大幅下降。设备端和中间件看起来“完全不对等”,但它们之间是分工关系,不是替代关系——设备负责采数,Pulsar 负责把数据变成可供分析和检索的资源,各自做好自己的角色。

这让我想到,技术选型里最容易犯的错误不是选错某个组件,而是用单一的视角去看整个系统,只站在自己的那一段里做判断。这次 COSCon’25 x Pulsar Developer Day 专场,之所以让我觉得值得记录,就是因为它把“设备端采集”和“服务端消息中间件”两个平时各说各话的世界放到了一起,让硬件工程师和后端工程师有机会在同一张桌子上讨论一条完整的数据通道。

5.2 给打算实践的人的三个提醒

如果看完这篇回顾,你也想在项目里尝试这条路——用 Pulsar 作为消息底座,或者用 STM32 采集环境数据接入消息服务——我有几个从现场和过往实操中总结出来的建议:

第一,启动项目时就把消息轨迹和死信队列配好。很多消息中间件的故障,最后追溯起来都是“没留证据”。Pulsar 的消息轨迹不算复杂,但一旦业务上线后再去补,成本和难度都会大很多。死信队列(DLQ)同理,消费失败的消息如果只是反复重试,积压迟早会把下游打爆。

第二,学会用多租户模型,而不是一把梭。Pulsar 的 tenant 和 namespace 是层级隔离的,建议直接用 tenant 隔开不同业务线团队,用 namespace 区分环境,配额管理和权限控制都要落到这个模型上。这不是多此一举,后期容量规划和成本核算都依赖这一层。

第三,从第一天就把 QoS、幂等和连接鉴权的设计一起想清楚,而不是先打通再说。传感器接入 MQTT 的时候要规划好 topic 命名规范、设备 ID 体系和 QoS 选择,这些基础设计一旦定了,后面很难改。先打通链路并无不妥,但“打通之后返工”的成本往往比一开始多想一步高得多。

5.3 一点个人体会

回顾这一天的感受,“Make MQ Great Again”之所以能引起这么多人共鸣,在于它抓住了大家心里一直存在的那个问题:消息队列不该只是一个“吞吐量很大”的管道,它应该有能力在不同场景下做出正确的取舍。Pulsar 能在这个会场里成为主角,是因为它的架构思路确实提供了不少破局的可能性,而 COSCon 这种开源社区氛围,又让技术讨论不再是厂商单方面的输出。

我个人最受益的,还是动手实验区里 STM32 接入 Pulsar 那套流程。它把硬件采集、协议转换、消息队列、消费应用串成了一条完整的链,跑通之后你就不会再觉得“消息队列是后端的事”了。后来我在自己项目里改 IoT 上报链路时,也在 DHT11 的采集逻辑里加了预热处理和异常重采样,这些小细节都是在现场讨论中得到的启发。技术文章的价值正在于此:你在自己的工程里踩过坑,再从别人的实践中获得新的视角,很多东西就真正通了。

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

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

立即咨询