一.Binding模块介绍
Binding模块是消息队列系统中的核心组件之一,它负责维护交换机(Exchange)与队列(Queue)之间的绑定关系。通过这个模块,我们可以灵活地定义消息的路由规则,实现消息的准确分发。
二.Binding的实现
Binding
结构体用于表示交换机与队列之间的绑定关系。它包含三个成员变量:_ename
表示交换机名称,_qname
表示队列名称,_binding_key
表示绑定键。
struct Binding {
using ptr = std::shared_ptr<Binding>;
std::string _ename;
std::string _qname;
std::string _binding_key;
Binding() {}
Binding(const std::string &ename, const std::string &qname, const std::string &key)
: _ename(ename), _qname(qname), _binding_key(key) {}
};
三.BindingMapper的实现
构造函数
BindingMapper
类的构造函数初始化数据库连接,并创建绑定表。
BindingMapper(const std::string &dbname)
: sql_helper(dbname)
{
FileHelper::createDir(FileHelper::getParentDirName(dbname));
if (!sql_helper.open())
{
ELOG("open binding db failed");
assert(0);
}
createTable();
}
创建和删除表
createTable
和 dropTable
方法分别用于创建和删除绑定表。
void createTable()
{
std::stringstream sql;
sql << "create table if not exists binding(";
sql << "ename varchar(32),";
sql << "qname varchar(32),";
sql << "binding_key varchar(128));";
if (!sql_helper.exec(sql.str(), nullptr, nullptr))
{
ELOG("create binding table failed");
assert(0);
}
}
void dropTable()
{
std::cout<<"drop binding table"<<std::endl;
std::stringstream sql;
sql << "drop table if exists binding;";
if (!sql_helper.exec(sql.str(), nullptr, nullptr))
{
ELOG("drop binding table failed");
assert(0);
}
}
插入和移除绑定
insert
方法用于将新的绑定关系插入到数据库中。
void insert(const Binding::ptr &ptr)
{
std::stringstream sql;
sql << "insert into binding(ename, qname, binding_key) values(";
sql << "'" << ptr->_ename << "',";
sql << "'" << ptr->_qname << "',";
sql << "'" << ptr->_binding_key << "');";
if (!sql_helper.exec(sql.str(), nullptr, nullptr))
{
ELOG("insert binding failed");
assert(0);
};
}
void remove(const std::string &ename, const std::string &qname)
{
std::stringstream sql;
sql << "delete from binding where ename='"
<< ename << "' and qname='"
<< qname << "';";
if (!sql_helper.exec(sql.str(), nullptr, nullptr))
{
ELOG("remove binding failed");
assert(0);
};
}
按交换机/队列名称移除绑定
移除交换机或队列符合要求的binding,可能不止一个
void removeByExchange(const std::string &ename)
{
std::stringstream sql;
sql << "delete from binding where ename='"
<< ename << "';";
if (!sql_helper.exec(sql.str(), nullptr, nullptr))
{
ELOG("remove binding failed");
assert(0);
};
}
void removeByQueue(const std::string &qname)
{
std::stringstream sql;
sql << "delete from binding where qname='"
<< qname << "';";
if (!sql_helper.exec(sql.str(), nullptr, nullptr))
{
ELOG("remove binding failed");
assert(0);
};
}
恢复绑定信息
recover
方法从数据库中恢复所有绑定信息,返回一个BindingMap
BindingMap recover()
{
BindingMap ret;
std::stringstream sql;
sql << "select * from binding;";
if (!sql_helper.exec(sql.str(), BindingMapperCb, &ret))
{
ELOG("recover binding failed");
assert(0);
}
return ret;
}
private:
static int BindingMapperCb(void *arg, int col_count, char **col_values, char **col_names)
{
BindingMap *ret = (BindingMap *)(arg);
// 先取出 exchange 对应的MsgQueueBindingMap
// 没有就创建哈希表,有就取出它的引用,若直接创建对象并插入,会覆盖原数据
MsgQueueBindingMap &map = (*ret)[col_values[0]];
Binding::ptr ptr = std::make_shared<Binding>(col_values[0], col_values[1], col_values[2]);
map[col_values[1]] = ptr;
return 0;
}
};
四.BindingManager的实现
主要成员变量
BindingMapper _mapper;
std::mutex _mutex;
BindingMap _bindings;
BindingManager
类在 BindingMapper
的基础上,提供了对绑定信息的管理功能,如绑定、解绑、查询等操作。它通过 std::mutex
实现了线程安全。
两个映射类型:
using MsgQueueBindingMap = std::unordered_map<std::string, Binding::ptr>; // queue -> binding
using BindingMap = std::unordered_map<std::string, MsgQueueBindingMap>; // exchange -> queue
MsgQueueBindingMap
:从队列名称到绑定的映射。BindingMap
:从交换机名称到MsgQueueBindingMap
的映射。
即每个exchange是唯一的,而queue可以重复使用,来找到不同的binding。
构造函数
BindingManager
类的构造函数从数据库恢复所有绑定信息。
BindingManager(const std::string &dbname)
: _mapper(dbname)
{
_bindings = _mapper.recover();
}
绑定与解绑
bind
方法
bind
方法用于在交换机与队列之间创建绑定关系,并根据需要将其持久化到数据库中。
- 首先检查该绑定关系是否已经存在。
- 如果不存在,则锁定
_mutex
,创建Binding
对象,并更新_bindings
映射。 - 如果
durable
参数为true
,则将绑定信息插入数据库进行持久化存储。
bool bind(const std::string &ename, const std::string &qname, const std::string &key, bool durable)
{
if (exists(ename, qname))
{
ILOG("%s,%s,%s exists", ename.c_str(), qname.c_str(), key.c_str());
return true;
}
std::unique_lock<std::mutex> lock(_mutex);
Binding::ptr bp = std::make_shared<Binding>(ename, qname, key);
auto &map = _bindings[ename];
map[qname] = bp;
if (durable)
_mapper.insert(bp);
return true;
}
unbind
方法
unbind
方法用于解除交换机与队列之间的绑定关系,并从数据库中删除相应的记录。
- 首先检查该绑定关系是否存在。
- 如果存在,则锁定
_mutex
,从_bindings
中删除该绑定关系,并调用_mapper
将其从数据库中删除。
bool unbind(const std::string &ename, const std::string &qname)
{
if (!exists(ename, qname))
{
ILOG("%s,%s not exists", ename.c_str(), qname.c_str());
return true;
}
std::unique_lock<std::mutex> lock(_mutex);
auto &map = _bindings[ename];
map.erase(qname);
_mapper.remove(ename, qname);
return true;
}
removeByExchange
removeByExchange
方法用于删除某个交换机与其所有队列之间的绑定关系。
- 该方法会锁定
_mutex
,从_bindings
中删除该交换机的所有绑定关系,并调用_mapper
从数据库中删除这些记录。
bool removeByExchange(const std::string &ename)
{
std::unique_lock<std::mutex> lock(_mutex);
_mapper.removeByExchange(ename);
_bindings.erase(ename);
return true;
}
removeByQueue
方法
removeByQueue
方法用于删除某个队列与所有交换机之间的绑定关系。
- 该方法会遍历所有的交换机,从每个交换机的绑定关系中删除指定队列的绑定关系,并更新
_bindings
。 - 同时,调用
_mapper
删除对应的数据库记录。
bool removeByQueue(const std::string qname)
{
std::unique_lock<std::mutex> lock(_mutex);
for (auto &kv : _bindings) // 遍历所有exchange的绑定
{
if (kv.second.find(qname) != kv.second.end())
{
kv.second.erase(qname); // 删除qname相关绑定
_mapper.remove(kv.first, qname);
}
}
return true;
}
查询绑定信息
selectByExchange
selectByExchange
方法用于获取某个交换机的所有绑定关系。
- 该方法会锁定
_mutex
,从_bindings
中查找指定交换机的绑定关系,并返回MsgQueueBindingMap
。
MsgQueueBindingMap selectByExchange(const std::string &ename)
{
std::unique_lock<std::mutex> lock(_mutex);
auto qit = _bindings.find(ename);
if (qit == _bindings.end())
{
ILOG("%s not exists", ename.c_str());
return MsgQueueBindingMap();
}
return qit->second;
}
getBinding
getBinding
方法用于获取某个交换机与某个队列之间的绑定关系。
- 该方法首先检查交换机是否存在于
_bindings
中。 - 如果存在,则进一步检查该队列是否与交换机绑定,并返回相应的
Binding
对象。
Binding::ptr getBinding(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 Binding::ptr();
auto map = eit->second;
auto qit = map.find(qname);
if (qit == map.end()) // 没找到
return Binding::ptr();
return qit->second;
}
其他管理操作
exists
方法
exists
方法用于检查某个交换机与队列之间的绑定关系是否存在。
- 该方法会锁定
_mutex
,在_bindings
中查找指定的绑定关系,并返回是否存在的布尔值。
bool exists(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 false;
auto map = eit->second;
auto qit = map.find(qname);
if (qit == map.end()) // 没找到
return false;
return true;
}
size
方法
size
方法返回当前绑定关系的总数量。
- 该方法会遍历
_bindings
,计算每个交换机的绑定关系数量,并返回总数
size_t size()
{
size_t cnt = 0;
std::unique_lock<std::mutex> lock(_mutex);
for (const auto &map : _bindings)
{
cnt += map.second.size();
}
return cnt;
}
clear方法
clear方法会删除binding表和清理内存的数据结构。
void clear()
{
std::unique_lock<std::mutex> lock(_mutex);
_mapper.dropTable();
_bindings.clear();
}
五.binding.hpp全部代码
#pragma once
#include "../common_mq/helper.hpp"
#include "../common_mq/logger.hpp"
#include "../common_mq/msg.pb.h"
#include <string>
#include <unordered_map>
#include <mutex>
#include <memory>
#include <cassert>
#include <cstring>
namespace mq
{
struct Binding
{
using ptr = std::shared_ptr<Binding>;
std::string _ename;
std::string _qname;
std::string _binding_key;
Binding() {}
Binding(const std::string &ename, const std::string &qname, const std::string &key)
: _ename(ename), _qname(qname), _binding_key(key)
{
}
};
using MsgQueueBindingMap = std::unordered_map<std::string, Binding::ptr>; // queue -> binding
using BindingMap = std::unordered_map<std::string, MsgQueueBindingMap>; // exchange -> queue
class BindingMapper
{
private:
SqliteHelper sql_helper;
public:
BindingMapper(const std::string &dbname)
: sql_helper(dbname)
{
FileHelper::createDir(FileHelper::getParentDirName(dbname));
if (!sql_helper.open())
{
ELOG("open binding db failed");
assert(0);
}
createTable();
}
// 1.创建/删除表
void createTable()
{
std::stringstream sql;
sql << "create table if not exists binding(";
sql << "ename varchar(32),";
sql << "qname varchar(32),";
sql << "binding_key varchar(128));";
if (!sql_helper.exec(sql.str(), nullptr, nullptr))
{
ELOG("create binding table failed");
assert(0);
}
}
void dropTable()
{
std::cout<<"drop binding table"<<std::endl;
std::stringstream sql;
sql << "drop table if exists binding;";
if (!sql_helper.exec(sql.str(), nullptr, nullptr))
{
ELOG("drop binding table failed");
assert(0);
}
}
// 2.插入绑定,移除指定/相关绑定
void insert(const Binding::ptr &ptr)
{
std::stringstream sql;
sql << "insert into binding(ename, qname, binding_key) values(";
sql << "'" << ptr->_ename << "',";
sql << "'" << ptr->_qname << "',";
sql << "'" << ptr->_binding_key << "');";
if (!sql_helper.exec(sql.str(), nullptr, nullptr))
{
ELOG("insert binding failed");
assert(0);
};
}
void remove(const std::string &ename, const std::string &qname)
{
std::stringstream sql;
sql << "delete from binding where ename='"
<< ename << "' and qname='"
<< qname << "';";
if (!sql_helper.exec(sql.str(), nullptr, nullptr))
{
ELOG("remove binding failed");
assert(0);
};
}
void removeByExchange(const std::string &ename)
{
std::stringstream sql;
sql << "delete from binding where ename='"
<< ename << "';";
if (!sql_helper.exec(sql.str(), nullptr, nullptr))
{
ELOG("remove binding failed");
assert(0);
};
}
void removeByQueue(const std::string &qname)
{
std::stringstream sql;
sql << "delete from binding where qname='"
<< qname << "';";
if (!sql_helper.exec(sql.str(), nullptr, nullptr))
{
ELOG("remove binding failed");
assert(0);
};
}
// 3.recover返回所有绑定信息
BindingMap recover()
{
BindingMap ret;
std::stringstream sql;
sql << "select * from binding;";
if (!sql_helper.exec(sql.str(), BindingMapperCb, &ret))
{
ELOG("recover binding failed");
assert(0);
}
return ret;
}
private:
static int BindingMapperCb(void *arg, int col_count, char **col_values, char **col_names)
{
BindingMap *ret = (BindingMap *)(arg);
// 先取出 exchange 对应的MsgQueueBindingMap
// 没有就创建哈希表,有就取出它的引用,若直接创建对象并插入,会覆盖原数据
MsgQueueBindingMap &map = (*ret)[col_values[0]];
Binding::ptr ptr = std::make_shared<Binding>(col_values[0], col_values[1], col_values[2]);
map[col_values[1]] = ptr;
return 0;
}
};
class BindingManager
{
public:
using ptr = std::shared_ptr<BindingManager>;
private:
BindingMapper _mapper;
std::mutex _mutex;
BindingMap _bindings;
public:
BindingManager(const std::string &dbname)
: _mapper(dbname)
{
_bindings = _mapper.recover();
}
// 1. 绑定/解除绑定
bool bind(const std::string &ename, const std::string &qname, const std::string &key, bool durable)
{
if (exists(ename, qname))
{
ILOG("%s,%s,%s exists", ename.c_str(), qname.c_str(), key.c_str());
return true;
}
// 再次加锁
std::unique_lock<std::mutex> lock(_mutex);
Binding::ptr bp = std::make_shared<Binding>(ename, qname, key);
auto &map = _bindings[ename];
map[qname] = bp;
// 持久化
if (durable)
_mapper.insert(bp);
return true;
}
bool unbind(const std::string &ename, const std::string &qname)
{
if (!exists(ename, qname))
{
ILOG("%s,%s not exists", ename.c_str(), qname.c_str())
return true;
}
// 再次加锁
std::unique_lock<std::mutex> lock(_mutex);
auto &map = _bindings[ename];
map.erase(qname);
// 持久化
_mapper.remove(ename, qname);
return true;
}
// 2. 删除Exchange/msgQueue相关绑定
bool removeByExchange(const std::string &ename)
{
std::unique_lock<std::mutex> lock(_mutex);
_mapper.removeByExchange(ename);
_bindings.erase(ename);
return true;
}
bool removeByQueue(const std::string qname)
{
std::unique_lock<std::mutex> lock(_mutex);
for (auto &kv : _bindings)// 遍历所有exchange的绑定
{
if (kv.second.find(qname) != kv.second.end())
{
kv.second.erase(qname);//删除qname相关绑定
_mapper.remove(kv.first, qname);
//erase后自动删除
// if (kv.second.empty())
// _bindings.erase(kv.first);
}
}
return true;
}
// 3. 获取Exchange相关绑定 / 获取特定绑定
MsgQueueBindingMap selectByExchange(const std::string &ename)
{
std::unique_lock<std::mutex> lock(_mutex);
auto qit = _bindings.find(ename);
if (qit == _bindings.end())
{
ILOG("%s not exists", ename.c_str());
return MsgQueueBindingMap();
}
return qit->second;
}
Binding::ptr getBinding(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 Binding::ptr();
auto map = eit->second;
auto qit = map.find(qname);
if (qit == map.end()) // 没找到
return Binding::ptr();
return qit->second;
}
// 4. 其它相关操作
bool exists(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 false;
auto map = eit->second;
auto qit = map.find(qname);
if (qit == map.end()) // 没找到
return false;
return true;
}
size_t size()
{
size_t cnt = 0;
std::unique_lock<std::mutex> lock(_mutex);
for (auto &it : _bindings)
{
cnt += it.second.size();
}
return cnt;
}
void clear()
{
std::unique_lock<std::mutex> lock(_mutex);
_mapper.dropTable();
_bindings.clear();
}
};
};