binding.hpp绑定模块

一.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();
        }

创建和删除表

createTabledropTable 方法分别用于创建和删除绑定表。

      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();
        }
    };
};

  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值