提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
数据管理模块需要管理四种数据,分别是交换机数据管理/队列数据管理/绑定关系数据管理/队列消息数据管理。
交换机数据管理
管理的字段
需要管理的数据有下面这个5个
交换机名称:交换机的唯一标识
交换机类型:交换机有三种类型,直接交换/广播交换/主题交换。决定了消息的转发方式。
持久化标识:决定了交换机信息是否持久化存储。方便断电后恢复。
剩下的俩个字段不需要关心,是为了以后进行扩展的。
cpp
struct Exchange
{
using ptr = std::shared_ptr<Exchange>;
std::string name; // 交换机名字
ExchangeType type; // 交换机类型
bool durable; // 交换机持久化标志位
bool auto_delete; // 交换机自动删除标志位 (还未实现该功能)
google::protobuf::Map<std::string, std::string> args; // 其他参数 (方便以后扩展)
}
持久化管理类
交换机的信息提供了持久化管理的操作,我们使用sqlite进行存储。
要管理一个sqlite的操作句柄,这个句柄对象也是我们封装了一下sqlite的操作。
在构造函数种需要传入一个文件路径,也就是存储交换机信息的文件。
sqlite是一个本地化的数据库,不需要通过网络客户端服务器的模式来进行通信。本地提供一个文件就可以进行存储。一个文件就相当于一个数据库database,可以在这个数据库种创建多个表。
我们的交换机/队列/绑定关系信息的数据都是存储在这个文件种的.
cpp
class ExchangeMapper
{
private:
SqliteHelper _sql_helper; //sqlite句柄
public:
ExchangeMapper(const std::string &dbfile)
: _sql_helper(dbfile)
{
// 创建父级目录
const std::string path = FileHelper::parentDirectory(dbfile);
FileHelper::createDirectory(path);
// 创建/打开数据库文件
assert(_sql_helper.open());
// 创建交换机数据表
createTable();
}
}
这就是创建的交换机表。
cpp
#define CREATE_TABLE "create table if not exists exchange_table(\
name varchar(32) primary key, \
type int, \
durable int, \
auto_delete int, \
args varchar(128));"
bool ret = _sql_helper.exec(CREATE_TABLE, nullptr, nullptr);
if (ret == false)
{
DLOG("创建交换机数据库表失败!!");
abort(); // 直接异常退出程序
}
这个类还提供一个恢复的接口,他会查询交换机表中的所有记录,存放到一个哈希表中。
cpp
//返回交换机表中所有数据,用于重启后恢复
std::unordered_map<std::string, Exchange::ptr> recovery(){
std::unordered_map<std::string, Exchange::ptr> res;
std::string sql = "select name, type, durable, auto_delete, args from exchange_table";
_sql_helper.exec(sql, selectCallBack, &res);
return res;
}
内存管理类
在内存管理类中包含了交换机持久化管理类的对象,和一个哈希表,用来管理已经存在交换机信息。
在他的构造函数中调用了持久化管理的数据恢复接口,他会查询数据库表中所有的字段,返回一个哈希表,也就完成了交换机数据恢复。
cpp
//交换机数据内存管理类,这个类才是对外提供的
class ExchangeManager
{
private:
std::mutex _mutex; //这个类对象可能被多线程访问,我们要加锁
ExchangeMapper _mapper;
std::unordered_map<std::string,Exchange::ptr> _Exchanges; //管理已经存在的交换机
ExchangeManager(const std::string &dbfile)
:_mapper(dbfile)
{
//恢复交换机
_Exchanges = _mapper.recovery();
}
申明交换机
在rabbitMQ中不叫创建交换机,而是叫做申明交换机,它是一种强断言的思想,代表着存在及ok,不存在就创建。这个操作也很简单。
先看看哈希表中存不存在,存在就返回true,不存在就构建一个交换机对象,插入哈希表
cpp
bool declareExchange(const std::string &name,
ExchangeType type, bool durable, bool auto_delete,
const google::protobuf::Map<std::string, std::string> &args)
{
std::unique_lock<std::mutex> lock(_mutex);
auto it = _Exchanges.find(name);
if(it != _Exchanges.end()){
//存在直接return
return true;
}
//定义一个Exchange对象
auto ecp = std::make_shared<Exchange>(name,type,durable,auto_delete,args);
//插入进数据库
if(durable == true) {
bool ret = _mapper.insert(ecp);
if(ret == false) return false;
}
_Exchanges.insert({name,ecp});
return true;
}
删除交换机
根据交换机的名称进行一个删除。同时如果持久化存储了,也要删除数据库中的数据。
cpp
void deleteExchange(const std::string &name){
std::unique_lock<std::mutex> lock(_mutex);
auto it = _Exchanges.find(name);
if(it == _Exchanges.end()){
return;
}
//删除数据库中数据
if(it->second->durable == true){
_mapper.remove(name);
}
_Exchanges.erase(name);
}
获取指定交换机
根据交换机姓名获取指定交换机。
cpp
//获取指定交换机
Exchange::ptr selectExchange(const std::string &name){
std::unique_lock<std::mutex> lock(_mutex);
auto it = _Exchanges.find(name);
if(it == _Exchanges.end()){
//交换机不存在
return Exchange::ptr();
}
return it->second;
}
队列数据管理
队列和交换机管理的思想几乎一致,只不过有些字段不一样
管理的字段
队列名称:队列的唯一标识
持久化标志。
其他字段是扩展字段,暂时不关心。
cpp
struct MsgQueue
{
using ptr = std::shared_ptr<MsgQueue>;
std::string name; // 队列名称
bool durable; // 持久化标志位
bool exclusive; // 是否独占 (还未实现此功能)
bool auto_delete; // 自动删除 (未实现)
google::protobuf::Map<std::string, std::string> args; // 其他参数
}
持久化管理类
和交换机一样,这里看一看表结构
cpp
void createTable()
{
std::stringstream sql;
sql << "create table if not exists queue_table(";
sql << "name varchar(32) primary key, ";
sql << "durable int, ";
sql << "exclusive int, ";
sql << "auto_delete int, ";
sql << "args varchar(128));";
assert(_sql_helper.exec(sql.str(), nullptr, nullptr));
}
内存管理类
都是一个持久化句柄,一个哈希表存储已经存在的队列信息。
cpp
class MsgQueueManager
{
private:
std::mutex _mutex;
MsgQueueMapper _mapper;
std::unordered_map<std::string, MsgQueue::ptr> _msg_queues;
public:
using ptr = std::shared_ptr<MsgQueueManager>;
MsgQueueManager(const std::string &dbfile) : _mapper(dbfile)
{
//从数据库中读取,恢复队列数据
_msg_queues = _mapper.recovery();
}
}
申明/删除/获取指定队列
这里不过多介绍,都是一样的操作
获取所有队列
但是队列这边提供了一个额外的操作,获取所有队列信息。
这里直接构造一个哈希表返回。
我们的队列消息和消费者都是以队列为单元进行管理的。所以我们需要获取到已经存在队列,用来初始化队列消息和消费者管理。
cpp
//返回所有队列
std::unordered_map<std::string, MsgQueue::ptr> AllQueue()
{
std::unique_lock<std::mutex> lock(_mutex);
return _msg_queues; //这里构造了一个
}
绑定关系管理
管理的字段
交换机名称和队列名称,还有一个binding_key。
cpp
struct Binding
{
using ptr = std::shared_ptr<Binding>;
std::string exchange_name; // 交换机名称
std::string msgqueue_name; // 队列名称
std::string binding_key;
}
持久化管理类
绑定关系也是需要持久化管理的。当交换机和队列的持久化标志位都为true时,我们才将绑定关系持久化管理。这在虚拟机管理模块进行判断。
cpp
void createTable()
{
std::stringstream sql;
sql << "create table if not exists binding_table(";
sql << "exchange_name varchar(32), ";
sql << "msgqueue_name varchar(32), ";
sql << "binding_key varchar(128));";
assert(_sql_helper.exec(sql.str(), nullptr, nullptr));
}
内存管理类
内存管理类这块也是一个持久化管理句柄。
但是我们这里是交换机和队列的一个信息管理。
因为一个交换机可以绑定多个队列,而队列和绑定关系是一一对应的。
所以我们定义了两个类型,一个是队列和绑定的映射。一个是交换机和队列绑定的映射。我们实际管理的就是这个交换机和队列绑定的对象。
cpp
using MsgQueueBindingMap = std::unordered_map<std::string, Binding::ptr>;
using BindingMap = std::unordered_map<std::string, MsgQueueBindingMap>;
cpp
class BindingManager
{
private:
std::mutex _mutex;
BindingMapper _mapper;
BindingMap _bindings;
using ptr = std::shared_ptr<BindingManager>;
BindingManager(const std::string &dbfile) : _mapper(dbfile)
{
_bindings = _mapper.recovery();
}
}
绑定/解除绑定
需要提供交换机名和队列名称以及binding_key和是否持久化。
这里的是否持久化是虚拟机判断后传入的。
这里也是存在及ok,不存在就创建的思想。
cpp
bool bind(const std::string &ename, const std::string &qname, const std::string &key, bool durable)
{
std::unique_lock<std::mutex> lock(_mutex);
auto eit = _bindings.find(ename);
if (eit != _bindings.end() && (eit->second.find(qname) != eit->second.end()))
{
//绑定数据已存在
return true;
}
Binding::ptr bp = std::make_shared<Binding>(ename, qname, key);
// 当交换机和队列的持久化标志位都为true,绑定数据才进行持久化,这个durable由外界判断后传入
if (durable == true)
{
bool ret = _mapper.insert(bp);
if (ret == false)
{
return false;
}
}
//存在及获取,不存在及创建
auto &MsgQueueMap = _bindings[ename];
MsgQueueMap.insert({qname, bp});
return true;
}
删除绑定关系,一个交换机可以绑定多个队列,这里删除的只是一个交换机和队列的绑定关系。
cpp
void unbind(const std::string &ename, const std::string &qname)
{
std::unique_lock<std::mutex> lock(_mutex);
auto eit = _bindings.find(ename);
//没有交换机信息,直接退出
if(eit == _bindings.end()) { return; }
auto qit = eit->second.find(qname);
//没有队列相关信息,直接退出
if(qit == eit->second.end()){ return ;}
//判断持久化太麻烦了,所以这里不管持久化标志是否存在,我们直接去数据库中删除
_mapper.remove(ename,qname);
_bindings[ename].erase(qname);
}
删除指定交换机的所有绑定关系
当交换机被删除时,需要删除该交换机的所有绑定关系
cpp
//删除指定交换机的所有绑定数据 ---当删除交换机时需要删除交换机相关的所有绑定信息
void removeExchangeBindngs(const std::string &ename){
//同样的这里不判断持久化,直接操作
_mapper.removeExchangeBindings(ename);
auto eit = _bindings.find(ename);
if(eit == _bindings.end()){
//不存在直接return
return;
}
_bindings.erase(eit);
}
删除队列的所有绑定关系
当队列被删除后,需要删除该队列所有的绑定关系。
删除的方法就是遍历所有的交换机,因为一个队列是可以被多个交换机绑定的。
cpp
void removeMsgQueueBindings(const std::string &qname){
std::unique_lock<std::mutex> lock(_mutex);
//同样的这里不判断持久化,直接操作
_mapper.removeMsgQueueBindings(qname);
//遍历所有的交换机,因为一个交换机可以绑定多个队列,这个要删除的队列可能绑定了多个交换机
for(auto eit = _bindings.begin(); eit != _bindings.end(); eit++){
//eit->second是一个MsgQueueMap,这里判断的是这个队列是否存在这个map中,存在就删除
auto qit = eit->second.find(qname);
if(qit != eit->second.end()){
eit->second.erase(qit);
}
}
}
获取指定的队列的所有绑定关系
这个接口非常重要,当交换机收到信息后,我们需要获取该交换机的所有绑定的队列,用来判断需要将消息转发到哪个队列。
cpp
//获取交换机绑定的队列描述信息,当交换机收到消息时,需要将消息转给绑定的队列
MsgQueueBindingMap getExchangeBindings(const std::string &ename){
std::unique_lock<std::mutex> lock(_mutex);
auto eit = _bindings.find(ename);
if (eit == _bindings.end()) {
return MsgQueueBindingMap();
}
return eit->second;
}
队列消息管理
管理的字段
这个模块有一些复杂,消息是需要被传输的,因此我们定义在了proto中,我们先看一下消息类型.
消息分为消息属性和消息正文。
消息属性中有三个字段,分别是消息的ID,routing_key和持久化模式。
消息正文就是一个string字段。
还有一个消息是否有效标志位。
也就是消息属性,消息正文,消息是否有效这三个字段是需要持久化存储的。我们把它定义为Payload字段。
其他两个字段是方便服务器进行管理二添加的字段。
cpp
//消息属性
message BasicProperties {
string id = 1; //消息ID
string routing_key = 2; //与binding_key做匹配
DeliveryMode delivery_mode = 3; //持久化模式 1-⾮持久化; 2-持久化
};
//消息结构
message Message {
message Payload {
BasicProperties properties = 1; //消息属性
string body = 2; //有效载荷数据
string valid = 3; //消息是否有效位
};
Payload payload = 1; //真正持久化的只有这⼀个字段
uint64 offset = 2; //这两个字段⽤于记录消息在持久化⽂件中的位置和⻓度
uint64 length = 3; //以便于在加载时可以在指定位置读取指定⻓度的数据获取到消息
};
持久化管理类
同样的消息也是需要进行持久化的,但是消息的持久化我们不是放在数据库中,而是存储在文件中。
因为有些消息会很大,不适合放在数据库。其次我们只是为了备份,不涉及到查询。
另外的我们的消息是以队列为单元进行管理的。
这是消息进行持久化管理的类,他有三个成员,一个是队列名称,另外两个是持久化数据文件的文件名。文件名就是用队列名加上.mqd后缀。
例如qname.mqd,这里还有一个tmpfile,原因是我们垃圾回收不是在源文件直接操作,而是遍历源文件,提取出有效消息,存入临时文件,最后在更改临时文件名称覆盖源文件。
cpp
class MessageManpper
{
private:
std::string _qname; // 队列名称
std::string _datafile; // 保存消息持久化数据文件
std::string _tmpfile; // 垃圾回收时的临时文件
}
插入消息数据
当收到一个消息后,如果消息的持久化标志位true,就需要对消息进行持久化存储了。
调用虚拟机的消息发布接口,然后调用到队列消息总的内存管理类。然后调用具体的一个队列消息管理类中的插入接口,进行插入。如果消息的持久化标志位是true,就需要持久化。
cpp
bool insert(MessagePtr &msp)
{
insert(_datafile, msp);
}
需要传入一个Message对象,这个对象是在队列消息管理的插入接口构造的.
这里的大致流程,将消息结构中的Payload结构序列化,然后获取到文件的大小,也就是我们要要存入的偏移量,在偏移量的位置先写入4字节的消息大小,也就是Payload的大小,然后写入序列化的数据。
最后会把偏移量和消息大小设置到MessagePtr中,这个Ptr会存储到消息链表和持久化哈希表中,也就同步跟新到内存了。
这里的偏移量是跳过4字节的,也就是指向的Payload。
cpp
bool insert(const std::string &file, MessagePtr &msp)
{
// 将消息数据中的有效载体序列化
std::string body = msp->payload().SerializeAsString();
FileHelper helper(file);
size_t fsize = helper.size();
size_t msg_size = body.size();
// 往文件写入4字节数据长度
bool ret = helper.write((char *)&msg_size, fsize, sizeof(size_t));
if (ret == false)
{
DLOG("往队列文件 %s 写入数据长度失败", file.c_str());
return false;
}
// DLOG("往队列文件 %s 写入数据长度:%d",file.c_str(),msg_size);
// 往文件写入消息有效载荷
ret = helper.write(body.c_str(), fsize + sizeof(size_t), msg_size);
if (ret == false)
{
DLOG("往队列文件 %s 写入数据主体失败", file.c_str());
return false;
}
// DLOG("往队列文件 %s 写入数据:%d",file.c_str(),body.c_str());
// 更改消息数据的偏移量和长度
msp->set_offset(fsize + sizeof(size_t));
msp->set_length(msg_size);
return true;
}
删除消息
当消息被确认后,就需要从文件中删除消息。而删除不是从文件中真的删除,而是将消息的有效位置0后,覆盖掉原文件中的消息。
这里需要传入一个MessagePtr,这个ptr就是收到了消费客户端的确认应答后,根据消息id在待确认哈希中找到对应的MessaePtr对象.
如果这个对象的持久化标志位1,就需要删除文件中的数据。
删除的流程就是将MessagePtr中的有效位置0,然后根据偏移量和消息长度,进行一个覆盖写入。
cpp
bool remove(MessagePtr &msp)
{
// 将消息数据的有效位设置位'0'
msp->mutable_payload()->set_valid("0");
// 对msg进行序列化
std::string body = msp->payload().SerializeAsString();
if (body.size() != msp->length())
{
DLOG("不能修改文件中的数据信息,因为新生成的数据与原数据长度不一致!");
return false;
}
FileHelper helper(_datafile);
// 将消息数据覆盖写入到文件位置
bool ret = helper.write(body.c_str(), msp->offset(), body.size());
if (ret == false)
{
DLOG("向队列数据文件 %s 写入数据失败", _datafile);
return false;
}
return true;
}
垃圾回收
这个垃圾回收会返回一个MessagePtr的链表,他会从文件中循环读取消息。先算出文件的大小,从0偏移量开始读取,先读取4字节消息长度,然后根据长度读取消息payload,盘后反序列化出一个Payload结构,判断有效位是否为1,为1则插入到链表中。循环结束就可以得到有效消息的链表。
然后将有效消息写入到临时文件,在写入到临时文件中消息的偏移量发生了改变,在内存中也存储了消息对象,所以需要同步更新消息的偏移量,所以我们的返回值是一个MessagePtr的链表。队列消息管理在进行了垃圾回收后可以进行跟新偏移量。
cpp
std::list<MessagePtr> gc()
{
std::list<MessagePtr> result;
bool ret = load(result);
if (ret == false)
{
DLOG("加载有效数据失败!\n");
return result;
}
// DLOG("加载有效数据结束,数据个数:%d",result.size());
// 将有效数据写入临时文件
FileHelper::createFile(_tmpfile); //必须先创建出临时文件,在datafile中没有数据时,不会进这个循环。会导致文件源文件被删除,tmp文件也没了
FileHelper tmp_file_helper(_tmpfile);
size_t offset = 0;
for (auto &msp : result)
{
ret = insert(_tmpfile, msp);
if (ret == false)
{
DLOG("向临时文件写入消息数据失败!!");
return result;
}
}
// DLOG("像临时文件写入数据结束,临时文件大小:%d",tmp_file_helper.size());
ret = FileHelper::removeFile(_datafile);
if (ret == false)
{
DLOG("删除源文件失败!");
return result;
}
// 4. 修改临时文件名,为源文件名称
ret = FileHelper(_tmpfile).rename(_datafile);
if (ret == false)
{
DLOG("修改临时文件名称失败!");
return result;
}
// 5. 返回新的有效数据
return result;
}
队列消息内存管理类
队列消息是以队列为单元进行管理的,所以这个类是存在一个队列就要有一个。
和交换机/队列/绑定关系不同,这里复杂一些.
管理的字段
需要有一个持久化句柄,消息需要持久化。
然后一个队列名,该类所代表的队列。
一个有效消息数量和总消息数量。用来进行垃圾回收
一个待推送消息链表,收到消息后插入这个链表中。
一个持久化哈希表,用来垃圾回收后更新内存中消息的偏移量
一个待确认哈希表,当消息推送消费者后,需要把消息从带推送链表中删除,然后插入到待确认哈希表中。
cpp
class QueueMessage
{
private:
MessageManpper _manpper; //持久化操作的句柄
std::string _qname; // 队列名
size_t _valid_count; // 有效消息数量
size_t _total_count; // 消息总数量
std::mutex _mutex;
std::list<MessagePtr> _msgs; // 带推送消息
std::unordered_map<std::string, MessagePtr> _durable_msgs; // 持久化消息
std::unordered_map<std::string, MessagePtr> _waitack_msgs; // 待确认消息
}
插入消息
插入消息是在服务器收到消息发布请求后,通过虚拟机句柄调用的,我们这里需要构造一个MEssagePTr.用户的请求中可能没有填入消息属性。如果没有填入的话,我们就构造一个属性字段填入,其中消息ID自动生成,持久化标志看队列是否持久化,routing_ket设置为空字符串。
cpp
bool insert(const BasicProperties *bp, std::string body, bool queue_is_durable)
然后我们根据持久化标志位,将消息持久化存储,同时更新消息数量,然后插入进带推送消息链表中。
删除消息
是在客户端确认后,删除待确认哈希表中的消息。
如果消息时持久化的,需要删除持久化信息,同同时删除消息数量。
另外进行一次垃圾回收,垃圾回收需要满足总体消息数量达到2000条,且有效消息数量的个数不到总消息的%50;
垃圾回收就是调用持久化管理的gc接口,它会返回一个list< MessagePtr>,通过这个list,跟新内存消息的偏移量(遍历list,find查找持久化哈希表中的Message,进行更新)。同时更新消息数量。
cpp
void gc()
{
if (GCCheck() == false)
return;
// 获取有效消息
std::list<MessagePtr> msgs = _manpper.gc();//这里构造了一个list<MessagePtr>,他和我们的_durable_msgs中的MessagePtr是不同的,所以需要单独更改_durable_msgs中MessagePtr的偏移量
for (auto &msg : msgs)
{
auto it = _durable_msgs.find(msg->payload().properties().id());
if (it == _durable_msgs.end())
{
// 持久化文件中的消息,在内存中不存在
DLOG("垃圾回收后,有一条持久化消息,在内存中没有进行管理");
_msgs.push_back(it->second); // 做法:将该消息插入进待推送链表的末尾
_durable_msgs.insert({msg->payload().properties().id(), msg});
continue;
}
// 更新每一条消息的实际存储位置
it->second->set_offset(msg->offset());
it->second->set_length(msg->length());
}
// 3. 更新当前的有效消息数量 & 总的持久化消息数量
_valid_count = _total_count = msgs.size();
}
获取队首消息
当队列收到一个消息,就需要进行推送,删除消息链表的头部消息,然后插入到待确认哈希表中。
cpp
// 获取队首消息
MessagePtr front()
{
std::unique_lock<std::mutex> lock(_mutex);
if(_msgs.size() == 0){
return MessagePtr();
}
// 从待推送链表中取出一个消息
MessagePtr msp = _msgs.front();
_msgs.pop_front();
// 并将该消息添加进待确认哈希表
_waitack_msgs.insert({msp->payload().properties().id(), msp});
return msp;
}
总的消息内存管理类
一个文件路径,我们的消息持久化文件的路径。
一个哈希表,队列名称和队列消息管理类的映射。
我们对外提供的就是这个对象。
cpp
class MessageManager
{
private:
std::string _basedir; //存储队列信息文件的路径,我们的队列信息是存储在文件中的
std::mutex _mutex;
std::unordered_map<std::string, QueueMessage::ptr> _queue_msgs; //队列名对应队列信息
}
在他的构造中,我们需要根据已经存在的队列创建出队列消息管理类。然后进行一个持久化消息的恢复。
cpp
// 初始化一个队列消息管理,在创建队列的时候调用
void InitQueueMessage(const std::string &qname)
{
QueueMessage::ptr tmp;
{
std::unique_lock<std::mutex> lock(_mutex);
auto it = _queue_msgs.find(qname);
if (it != _queue_msgs.end())
{
//DLOG("消息管理句柄 %s 已经存在", qname.c_str());
return;
}
// 构建一个队列消息内存管理类对象
QueueMessage::ptr qmp = std::make_shared<QueueMessage>(_basedir, qname);
tmp = qmp;
_queue_msgs.insert(std::make_pair(qname, qmp));
}
// 恢复历史数据,这个操作是非常耗时的,我们没有放在加锁里。
tmp->recovery();
}
这里不想交换机,队列和绑定关系的恢复那么轻量,这里的消息数量会很大,因此我们没有在队列消息的构造函数中进行,而是单独提供了一个接口用来进行恢复。
恢复就是进行一次垃圾回收,然后返回一个链表,遍历链表,插入到内存管理中。
删除一个队列消息管理
当一个队列删除时,他的队列消息管理也就没有意义了,需要删除他的管理,同时删除对应的持久化文件数据。
cpp
// 销毁一个队列消息管理,在删除队列的时候调用
void DestoryQueueMessage(const std::string &qname)
{
QueueMessage::ptr tmp;
{
std::unique_lock<std::mutex> lock(_mutex);
auto it = _queue_msgs.find(qname);
if (it == _queue_msgs.end())
{
DLOG("消息管理句柄 %s 不存在", qname.c_str());
return;
}
tmp = it->second;
_queue_msgs.erase(qname);
}
tmp->clear();
}
新增消息
这里都是对指定队列消息管理进行的操作。接口我们已经实现了,就是从哈希表中找到 指定的对象就行。
cpp
// 新增一条消息
bool insert(const std::string &qname, BasicProperties *bp, std::string body, bool queue_is_durable)
{
QueueMessage::ptr tmp;
{
std::unique_lock<std::mutex> lock(_mutex);
auto it = _queue_msgs.find(qname);
if (it == _queue_msgs.end())
{
DLOG("向队列%s新增消息失败:没有找到消息管理句柄!", qname.c_str());
return false;
}
tmp = it->second;
}
return tmp->insert(bp, body, queue_is_durable);
}
获取队首消息
cpp
MessagePtr front(const std::string &qname)
{
QueueMessage::ptr tmp;
{
std::unique_lock<std::mutex> lock(_mutex);
auto it = _queue_msgs.find(qname);
if (it == _queue_msgs.end())
{
DLOG("向队列%s获取队首消息失败:没有找到消息管理句柄!", qname.c_str());
return MessagePtr();
}
tmp = it->second;
}
return tmp->front();
}
确认消息
cpp
void ack(const std::string &qname, const std::string &msg_id)
{
QueueMessage::ptr tmp;
{
std::unique_lock<std::mutex> lock(_mutex);
auto it = _queue_msgs.find(qname);
if (it == _queue_msgs.end())
{
DLOG("向队列%s确认消息失败:没有找到消息管理句柄!", qname.c_str());
return;
}
tmp = it->second;
}
tmp->remove(msg_id);
return;
}