mqtt-plus 架构解析(十):从内部项目到开源框架,mqtt-plus 的抽取过程与决策

张开发
2026/4/13 16:47:39 15 分钟阅读

分享文章

mqtt-plus 架构解析(十):从内部项目到开源框架,mqtt-plus 的抽取过程与决策
mqtt-plus 架构解析十从内部项目到开源框架mqtt-plus 的抽取过程与决策摘要很多框架的开源并不是“把内部代码推到 GitHub”这么简单。真正困难的地方在于哪些能力是通用框架能力哪些仍然带着业务包袱哪些 API 在内部能凑合用到了开源之后却必须重新定义哪些范围应该先做稳哪些需求应该刻意延后。mqtt-plus的形成就是这样一次抽取与重设边界的过程。本文会基于整个系列前 9 篇已经拆开的模块与实现回到更高层的问题mqtt-plus 为什么能从drone-framework里的 MQTT 能力沉淀成一个独立开源框架以及这一路上做了哪些关键取舍。项目地址项目地址https://github.com/mqttplus/mqtt-plus配套的示例工程https://github.com/mqttplus/mqtt-plus-examples如果你对这个方向感兴趣欢迎关注、试用也欢迎一起交流 issue 和 PR。如果这篇文章对你有帮助欢迎点赞、收藏也欢迎给项目一个 Star。到了第 10 篇系列终于回到了起点。前面 9 篇其实一直在做同一件事把 mqtt-plus 现在的结构拆开把每个模块边界讲清楚把每一处实现取舍落到真实源码上但这些文章还有一个更大的隐含问题为什么 mqtt-plus 会长成现在这样为什么它不是一个“顺手做出来的工具包”而是一组边界比较清楚的模块为什么它的很多实现看起来都偏克制像是刻意砍掉了一些“也许能做”的能力答案其实藏在它的来源里mqtt-plus 不是从空白画布上设计出来的理想框架而是从内部项目里的重复能力、混杂边界和长期维护痛点里一步步抽出来的。这也决定了第 10 篇应该讲的不是“起源故事”而是抽取方法本身。一、这篇文章到底想回答什么这一篇只回答三个问题哪些能力应该从内部项目里抽出来成为通用框架能力哪些业务包袱必须留在原项目里而不能带进开源框架mqtt-plus 为什么会选择今天这种边界清楚、范围克制、模块分层明确的形态如果只记住一句话那就是mqtt-plus 的开源不是把内部代码复制出来而是先把“什么属于框架、什么属于业务”这条边界重新画了一遍。二、先看从内部项目到开源框架的演进图先把整个演进过程压成一张图。drone-framework: 4 MQTT-related modulesRepeated connection / listener / bridge logicIdentify reusable framework concernsSeparate business logic from framework logicRedesign stable public abstractionsSplit into layered modulesmqtt-plus open-source framework这张图里最重要的不是“最后开源了”而是中间那三步识别可复用能力把业务逻辑和框架逻辑切开重新设计稳定抽象如果少了任何一步最终都很容易变成不是框架而是一份“脱敏过的内部代码”看起来能复用实际上边界还是内聚在原业务模型上开源之后 API 很快失稳因为它本来就不是为外部用户设计的这也是 mqtt-plus 现在很多“克制感”的真正来源。三、第一步不是开源而是先识别“什么算框架能力”从第 1 篇开始我们就反复提到mqtt-plus 的前身来自drone-framework里 4 个 MQTT 相关模块。那一步真正暴露出来的问题不是“模块数量多”而是连接能力在重复listener 注册和调用逻辑在重复订阅恢复逻辑在重复错误处理、转换链和 broker 管理都开始散落在不同模块里一旦系统走到这里就会出现一个很典型的信号这些东西已经不再是“某个业务场景的实现细节”而是开始表现出框架级共性。也就是说真正值得抽出来的不是“某个设备模型”或者“某个领域 topic 规范”而是这些在不同模块里都要重复回答的问题broker 连接如何抽象listener 如何注册和路由payload 如何转换连接恢复和动态订阅如何协调Spring 如何与 core 能力粘合这一层识别非常关键因为很多内部项目的开源失败不是因为代码差而是因为第一步就抽错了把业务场景里的偶然性误当成了框架能力把内部项目里临时可用的 API 误当成了通用抽象mqtt-plus 现在能维持比较稳定的模块边界本质上就是因为第一步抽出来的基本都是“跨场景重复成立”的问题。四、第二步更难哪些东西必须留在原项目里一个内部项目要开源最难的地方往往不是“什么能抽出来”而是什么不能带出来。如果把这一步做不好开源项目很容易出现两种问题明明是框架却带着大量业务命名、业务默认值和场景耦合明明想做通用能力却不断为原项目的历史兼容包袱服务mqtt-plus 现在的边界其实已经很能说明这个取舍。比如当前仓库里被明确放进“当前范围之外”的就有mqtt-plus-hivemqMQTT 5.0 支持运行时动态修改 broker 连接信息这些能力不是不重要而是它们要么还没有达到稳定抽象状态要么还不适合进入当前这一版框架的公共承诺。同样像设备领域对象、行业 topic 规范、无人机场景里的专属桥接逻辑也没有跟着一起进入开源框架。这说明 mqtt-plus 在抽取时其实做了一个很重要的动作把“框架共性”抽出来把“原项目负担”留在原项目里YesNoYesNoExisting internal MQTT codeBusiness-specific?Keep in original projectReusable framework concern?Extract into mqtt-plusDiscard or redesign这张图的价值在于它说明了一个很现实的开源原则不是所有内部代码都值得开源很多代码更适合被留在原项目里或者干脆在抽取时重写。设计决策mqtt-plus 没有试图把内部项目里所有 MQTT 相关代码一并带出来而是刻意把业务模型、场景特定桥接逻辑和尚未稳定的能力留在原项目或延后范围之外。这样做的重点是让开源框架只对真正稳定、可复用的能力负责。五、第三步开源不是复制代码而是重新设计公共抽象真正决定 mqtt-plus 能不能成立的其实不是“有没有代码”而是有没有重新设计可以对外承诺的抽象有没有把内部实现里的偶然写法收敛成稳定 API这一点从整个系列里已经很容易看出来。比如这些对象与接口本质上就不是“随手提炼”的结果而是一次重新设计后的公共边界MqttClientAdapterMqttClientAdapterFactoryMqttListenerRegistryMqttMessageRouterPayloadConverter/PayloadSerializerMqttSubscriptionManagerMqttSubscriptionReconcilerMqttTemplate这些抽象有一个共同特点它们不带业务词汇它们不直接绑定某个具体客户端实现它们足够小但能拼成完整链路这其实就是“内部可用代码”和“开源框架代码”的本质区别。内部代码很多时候只需要先让当前项目跑起来兼容现有调用方对外部使用者没有稳定承诺压力但开源框架不一样。它需要的是抽象名字清楚依赖方向长期稳定模块边界足够清晰扩展点能让别人理解并接入这也是为什么 mqtt-plus 最终会长成coreadapterspringstartertest这类结构而不是简单把原来那 4 个内部模块换个名字重新发布。六、第四步开源后的边界为什么反而更克制很多人对开源有一个误解觉得一旦开源就应该“把能做的都做进去”。但对框架来说真正更成熟的做法往往相反越是对外承诺就越要控制范围越是想稳定演进就越要先收住边界mqtt-plus 当前的范围其实就很能说明这一点。从 README 可以看到这一版明确写了已包含mqtt-plus-core、mqtt-plus-paho、mqtt-plus-spring-integration、mqtt-plus-spring、mqtt-plus-spring-boot-starter、mqtt-plus-test暂缓mqtt-plus-hivemq、MQTT 5.0、运行时动态修改 broker 连接信息这说明开源后的第一优先级并不是“覆盖一切场景”而是先把当前边界内的能力做到结构稳定让使用者知道什么已经是承诺什么还不是避免在还没稳定之前就把过多路线一口气暴露成公共 API如果结合前 9 篇一起看你会发现这种克制几乎贯穿全系列interceptor 只做前后钩子不承担错误决策ErrorAction已经建模但协议层动作闭环还没完全打通动态订阅恢复模型已经有了但动态 qos 还没被完整持久化starter 聚焦装配不把所有策略都参数化这些都不是“没做完”的简单表象更像是一种很清楚的开源节奏先把边界定义对再逐步扩张能力。设计决策mqtt-plus 开源后的范围比内部实现更克制不是因为能力不够而是因为对外 API 一旦形成承诺后续演进成本会急剧上升。先收住范围、先稳住边界是比“功能先做满”更理性的开源策略。七、从整个系列回看哪些取舍最能说明“这是一个框架”而不是“一份内部代码导出”如果把前 9 篇一起回看我觉得最能体现这个转变的至少有 5 个点。1. core 零框架依赖这说明开源后的第一承诺是稳定内核而不是先服务某个生态接入方式。2. adapter 可插拔这说明 transport 选择不是框架公共语义的一部分而是可替换实现的一部分。3. Spring 层只做粘合这说明框架没有把 Spring 经验直接写死进核心抽象里。4. 测试体系单独成模块这说明“如何验证框架”也被当成产品化能力而不是只在项目内部临时写点测试。5. 范围被明确写进 README这说明框架已经开始对外管理预期而不是无限延展。换句话说mqtt-plus 真正开源出来的不只是若干实现类而是一套对外可解释、可约束、可维护的结构。八、这一篇真正想给读者留下什么第 10 篇如果只停留在“它来自内部项目”其实信息量是不够的。我更希望它留下的是一个更通用的判断标准当你想把内部项目里的某部分能力抽成开源框架时真正该先问的不是现在这份代码能不能跑有没有人会 Star要不要先发一个版本而是哪些能力跨场景重复成立哪些能力只是当前业务的局部最优哪些边界已经稳定到值得对外承诺哪些范围还应该先留在项目内部慢慢长如果用这个标准回头看 mqtt-plus你会发现它最重要的地方其实不是“支持了哪些功能”而是它在抽取过程中先把边界想清楚了。这也是为什么整个系列虽然讲了很多实现细节但最后仍然会收束到一个更高层的结论好框架不是功能堆出来的好框架往往是边界收出来的九、小结第 10 篇是整个系列的收束篇。如果把它压缩成几句结论大概就是mqtt-plus 的起点不是空白设计而是内部项目里长期重复出现的 MQTT 共性问题真正被抽出来的不是所有内部代码而是那些跨场景可复用的框架能力真正被留下的不是“不重要”的部分而是那些仍带有业务负担、边界还不稳定、或者暂时不适合公共承诺的能力开源之后的 mqtt-plus 比内部实现更克制这不是退步而是为了让模块边界、API 承诺和后续演进都更稳如果前 9 篇回答的是“mqtt-plus 现在为什么这样工作”那第 10 篇回答的就是它为什么值得以现在这种形态存在。这也意味着整个系列到这里就闭环了。第 1 篇讲为什么这样分层第 2 到第 9 篇讲这些分层各自承载什么责任第 10 篇则回到源头解释这些边界为什么会被画成现在这样系列导航本文是mqtt-plus 架构解析系列的第 10/10 篇。#主题链接1总览分层架构与设计哲学链接2消息路由一条 MQTT 消息如何到达你的MqttListener链接3Payload 序列化与反序列化双链设计的取舍链接4拦截器链MqttMessageInterceptor的扩展点设计链接5错误处理ErrorAction聚合策略的设计逻辑链接6多 Broker 管理如何让一个应用同时连接多个 MQTT 服务链接7动态订阅与重连恢复Reconciler的协调机制链接8Spring Boot 自动装配零件是怎么被粘合起来的链接9测试体系MqttTestTemplate与EmbeddedBroker的设计链接10从内部项目到开源框架mqtt-plus 的抽取过程与决策本文上一篇测试体系MqttTestTemplate与EmbeddedBroker的设计下一篇无

更多文章