当前位置: 首页 > news >正文

用MessageBus优化模块通信:实现订阅/发布模式

  • 1、场景介绍
  • 2、为什么采用消息总线,订阅/发布模式的特点
  • 3、订阅/发布模式的介绍与代码演练
  • 总结

1、场景介绍

在复杂的软件系统中,我们常常需要多个模块协同工作。
在这里插入图片描述
比如上方有管理套接字的管理器和管理模块的管理器,可能涉及相互调用方法的场景,最常规的就是都创建后,将实例化对象传入到对方中,再相互调用方法。

问题是:

  • 如果模块之间直接互相调用,会出现严重的耦合
  • 一个模块改动,可能会影响很多其他模块。

解决办法就是引入 消息总线(MessageBus)

  • 所有模块都只和“总线”通信,不直接互相依赖。
  • 上位机发来的指令通过总线分发给相关模块。
  • 模块的状态也可以通过总线广播给感兴趣的对象。

2、为什么采用消息总线,订阅/发布模式的特点

消息总线采用 订阅/发布(Pub-Sub)模式

其核心思想是:

  • 订阅:模块声明自己对某类消息感兴趣,并注册一个回调函数。
  • 发布:某个模块发送消息到总线,总线会自动通知所有订阅者。

优点

  1. 解耦

    • 发布者不需要知道订阅者是谁。
    • 订阅者也不需要知道消息从哪来。
  2. 可扩展性强

    • 新增一个模块,只需订阅相关主题即可,无需改动原有模块。
  3. 灵活的消息流转

    • 一个消息可以广播给多个模块,天然支持一对多通信。

就像电台广播:

  • 广播站只需要往“频道”里发声音;
  • 所有调到该频道的收音机都能听到,不需要一个个单独通知。

3、订阅/发布模式的介绍与代码演练

所谓的订阅发布本质上就是维护一个👉 总线内部的字典:

subscribers[topic1] = {回调函数32, 回调函数52, ...}
subscribers[topic2] = {回调函数112, 回调函数12, ...}
subscribers[topic3] = {回调函数145, 回调函数62, ...}

当某个线程对 topic1 感兴趣时,它会通过调用 subscribe 方法向消息总线注册 topic1 及其对应的回调函数。随后,当系统中其他组件调用 publish 发布 topic1 的消息时,消息总线会在内部的 subscribers 字典中查找与 topic1 关联的所有回调函数,并依次执行这些回调函数,将消息对象传递给订阅者进行处理。

下面通过一个简化版的 MessageBus 演示消息总线的实现。

(1)消息结构

struct Message {std::string topic;   // 主题,如 "command" 或 "status"std::string payload; // 消息内容,可以是字符串/JSON/二进制数据
};

(2)消息总线

class MessageBus {
public:using Callback = std::function<void(const Message&)>;// 订阅主题:将回调函数存到 subscribers 表里void subscribe(const std::string& topic, Callback cb) {subscribers[topic].push_back(cb); // 将新注册的回调函数cb压入topic中}// 发布消息:找到所有订阅该主题的回调并依次调用void publish(const Message& msg) {// 发布消息时,遍历topic下所有回调函数,并执行if (subscribers.find(msg.topic) != subscribers.end()) {for (auto& cb : subscribers[msg.topic]) {cb(msg);}}}private:std::unordered_map<std::string, std::vector<Callback>> subscribers;
};

(3)类模块作为订阅者

我们以 转台模块 为例。

class Turret {
public:Turret(std::string name): name(name), angle(0) {}// 处理 command 消息void onCommand(const Message& msg) {std::cout << "[" << name << "] 收到命令: " << msg.payload << std::endl;angle += 10;std::cout << "[" << name << "] 当前角度: " << angle << std::endl;}private:std::string name;int angle;
};

(4)绑定成员函数作为回调

在订阅时,我们可以绑定类成员函数:

MessageBus bus;
Turret turret("转台模块");// 绑定成员函数作为回调
bus.subscribe("command", std::bind(&Turret::onCommand, &turret, std::placeholders::_1));// 发布一条消息
bus.publish({"command", "MOVE_TURRET 10 20"});

输出结果

[转台模块] 收到命令: MOVE_TURRET 10 20
[转台模块] 当前角度: 10

(5)也可以用 Lambda 捕获 this

现代 C++ 更常用 Lambda:

bus.subscribe("command", [&](const Message& msg) {turret.onCommand(msg);  // 调用对象的方法
});

这样写更简洁。


总结

  • 消息总线是一种发布/订阅模式的实现,核心是“主题→回调函数列表”的映射。
  • 订阅就是注册一个回调函数到总线;
  • 发布就是找到所有订阅该主题的回调并依次调用。
  • 成员函数完全可以作为回调,被调用时能访问对象的所有属性和方法。

通过消息总线,模块之间不再直接依赖,而是通过“消息”沟通,系统结构更清晰、可维护性更强。

http://www.dtcms.com/a/349611.html

相关文章:

  • nmcli命令详解
  • 文吃透朴素贝叶斯:从原理到实战
  • 【python文件处理】使用 open() 函数打开文件、 File 操作文件、使用 OS 对象操作文件目录的知识,使用 open() 函数打开文件
  • DMP-Net:面向脑组织术中成像的深度语义先验压缩光谱重建方法|文献速递-深度学习人工智能医疗图像
  • Android进入Activity时闪黑生命周期销毁并重建
  • 集成电路学习:什么是Caffe深度学习框架
  • 强化学习核心概念与算法详解-马尔可夫决策过程(MDP)+贝尔曼方程(Bellman Equation)
  • 合同管理软件的主要功能有什么?
  • 朴素贝叶斯学习笔记:从原理到实战(J享)
  • (LeetCode 每日一题) 498. 对角线遍历 (矩阵、模拟)
  • SSM从入门到实战:3.2 SpringMVC请求处理与控制器
  • 《C++哈希表:高效数据存储与检索的核心技术》
  • 朴素贝叶斯算法学习总结
  • MySQL 磁盘和 Redis 内存
  • 无人机航拍数据集|第22期 无人机城市交通目标检测YOLO数据集8624张yolov11/yolov8/yolov5可训练
  • Coze用户账号设置修改用户头像-前端源码
  • 【ACP】2025-最新-疑难题解析-5
  • Python Day 33 JavaScript BOM 与 DOM 核心笔记整合
  • 【数学建模】如何总结数学建模中的层次分析法最好
  • 通过Fiddler肆意修改接口返回数据进行测试
  • EXCEL自动调整列宽适应A4 A3 A2
  • OpenCV计算机视觉实战(21)——模板匹配详解
  • 将盾CDN:高防CDN和游戏盾有什么区别?
  • 宋红康 JVM 笔记 Day07|本地方法接口、本地方法栈
  • More Effective C++ 条款08:理解各种不同意义的new和delete
  • Genymotion 虚拟机如何安装 APK?(ARM 插件安装教程)
  • (操作系统)死锁是什么 必要条件 解决方式
  • 5分钟发布技术博客:cpolar简化Docsify远程协作流程
  • 《 nmcli网络管理学习》
  • [新启航]医疗器械深孔加工:新启航激光频率梳攻克 130mm 深度,实现 2μm 精度测量