0. 引言
消息总线(Message Bus)作为一种重要的通信模式,被应用于解耦系统中的组件,实现异步通信和事件驱动架构。
本文将介绍如何使用C++11实现一个非阻塞消息总线,通过异步处理消息来提高系统的并发性能和响应速度。
1. 设计方案
1.1 分析代码
头文件包含和类型定义
#pragma once
#include <map>
#include <vector>
#include <list>
#include <mutex>
#include "timer.hpp"
typedef std::function<void(std::string param1, int param2)> Callback_t;
typedef std::function<void()> TimeOutCallback_t;
enum CallbackType_t
{
ALWAYS = 0,
ONCE
};
struct CallbackItem_t
{
Callback_t callback = nullptr;
TimeOutCallback_t timeOutCallback = nullptr;
uint32_t timeoutInterval = 1000; // milliseconds
uint64_t timeoutStamp = 0; // microseconds
std::vector<int> msgNumVec;
CallbackType_t callbackType = ALWAYS;
};
- 类型定义: 定义了回调函数类型
Callback_t
和超时回调函数类型TimeOutCallback_t
,以及消息回调类型CallbackItem_t
,包含回调函数、超时回调、超时时间间隔、消息号列表和回调类型。
MessageBus 类定义
class MessageBus
{
public:
static MessageBus& instance()
{
static MessageBus ins;
return ins;
}
void publish(int msg, std::string param1, int param2 = 0);
void timeOutCheck();
bool subscribe(CallbackItem_t);
void reset();
void stop();
void start();
private:
MessageBus() = default;
MessageBus(const MessageBus&) = delete;
MessageBus& operator= (const MessageBus&) = delete;
typedef std::shared_ptr<CallbackItem_t> CallbackItem_ptr;
bool subscribe(int msg, CallbackItem_ptr);
bool unsubscribe(int msg, CallbackItem_ptr);
void regTimeOutCallback(CallbackItem_ptr);
typedef std::map<int, std::vector<CallbackItem_ptr>> CallbackMap_t;
CallbackMap_t _callbackMap; // message dispatch map
std::list<CallbackItem_ptr> _timeoutCheckList; // timeout check list
std::mutex _timeoutCheckListMutex;
std::mutex _callbackMapMutex;
Timer _timer;
};
-
成员变量:
_callbackMap
:消息号到回调函数列表的映射,用于存储订阅关系。_timeoutCheckList
:超时检查列表,存储需要检查超时的回调函数列表。_timeoutCheckListMutex
和_callbackMapMutex
:互斥量,用于保护_timeoutCheckList
和_callbackMap
的并发访问。_timer
:定时器对象,用于定期执行超时检查。
-
成员函数:
subscribe
和unsubscribe
:订阅和取消订阅消息。regTimeOutCallback
:注册超时回调函数。start
和stop
:启动和停止消息总线的超时检查。publish
:发布消息,触发相应的回调函数,并处理超时逻辑。timeOutCheck
:超时检查函数,定期检查超时并执行超时回调函数。
辅助函数
static uint64_t getTimeStamp()
{
std::chrono::microseconds ms = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::system_clock::now().time_since_epoch()
);
return ms.count();
}
getTimeStamp
:获取当前时间戳(微秒级)的静态辅助函数。
方法实现
subscribe
和unsubscribe
:通过_callbackMap
管理消息的订阅关系。regTimeOutCallback
:计算超时时间戳并将回调函数加入_timeoutCheckList
。start
和stop
:控制定时器的启动和停止。publish
:发布消息,根据消息号查找并执行相应的回调函数,同时处理超时逻辑。
1.2. 流程图
根据上述代码实现的功能,绘制如下流程图来说明消息总线的工作流程:
流程说明:
- MessageBus 是消息总线的核心控制器,负责处理订阅、取消订阅和发布消息等功能。
- Subscribe 和 Unsubscribe 分别用于添加和移除消息的订阅关系。
- Publish 通过查找
CallbackMap
执行消息的回调函数,并处理超时逻辑。 - Timer 控制定时器的启动和停止,定期触发超时检查。
- CallbackMap 存储了消息号与对应的回调函数列表。
- TimeoutCheckList 存储了需要进行超时检查的回调函数列表。
- CheckTimeout 检查超时,如果超时则执行相应的超时回调函数。
- ExecuteTimeoutCallback 执行超时时的回调函数逻辑。
3. 优缺点分析
优点:
- 高并发支持: 使用非阻塞队列和异步处理,能够有效支持高并发的消息处理需求,提高系统的吞吐量和响应速度。
- 低延迟: 避免了传统同步阻塞方式下可能出现的长时间等待,通过异步处理消息可以大大降低消息处理的延迟。
- 模块化设计: 使用模板和函数对象,支持灵活的消息类型和订阅行为,使得系统更易于扩展和维护。
缺点:
- 资源消耗: 需要额外的工作线程来处理消息队列,可能增加系统的资源消耗,特别是在高并发和大量消息处理的情况下。
- 复杂性增加: 需要处理线程安全性和消息处理的异步逻辑,可能增加代码的复杂度和维护成本,需要谨慎设计和测试。