routine.hpp路由匹配模块

一.路由匹配模块介绍

路由匹配模块可以验证路由键(routing key)和绑定键(binding key)的合法性,并根据不同的交换机类型(如DirectFanoutTopic)进行消息的路由匹配。

二.Routine类的实现

设计思路

在消息队列系统中,消息路由是一个关键的功能。
交换机根据路由键将消息发送到与其匹配的队列,而不同类型的交换机有不同的路由规则。Routine类的设计目标是处理这些规则:

  1. 合法性校验:验证路由键和绑定键的字符规则是否符合预期。例如,routing_key只允许字母、数字、点和下划线,而binding_key还允许通配符(*#)。
  2. 路由匹配算法:根据交换机类型,确定routing_key和binding_key是否匹配,特别是在Topic类型的交换机中,复杂的匹配规则需要使用动态规划来实现

代码细节分析

合法性校验

routineKeyValid()方法用于验证路由键是否合法,允许的字符包括字母、数字、点和下划线。
bindingKeyValid()方法则允许更多字符,包括通配符*#,并对绑定键的格式进行了更严格的检查。例如,*#必须独立作为单词出现,且#不能和其他通配符同时出现。

 static bool routineKeyValid(const std::string &rkey)
        {
            // 包含的字符规则: 0-9,a-z,A-Z, . _
            for (auto &ch : rkey)
            {
                if (ch >= '0' && ch <= '9' ||
                    ch >= 'a' && ch <= 'z' ||
                    ch >= 'A' && ch <= 'Z' ||
                    ch == '.' || ch == '_')
                    continue;
                else
                    return false;
            }
            return true;
        }
        static bool bindingKeyValid(const std::string &bkey)
        {
            // 1. 包含的字符规则 0-9,a-z,A-Z, . _ * #
            for (auto &ch : bkey)
            {
                if (ch >= '0' && ch <= '9' ||
                    ch >= 'a' && ch <= 'z' ||
                    ch >= 'A' && ch <= 'Z' ||
                    ch == '.' || ch == '_' ||
                    ch == '*' || ch == '#')
                    continue;
                else
                    return false;
            }
            // 2. 单词直接以.作为分隔符,#和* 必须独立作为单词出现
            std::vector<std::string> words;
            StrHelper::split(bkey, ".", words);
            for (auto &word : words)
            {
                if (word.size() > 1 &&
                    (word.find('*') != std::string::npos ||
                     word.find('#') != std::string::npos))
                    return false;
            }
            // 3. #可以匹配0-n个单词,即#前后不能有*和#的通配符
            for (int i = 1; i < words.size(); ++i)
            {
                if (words[i] == "#" && words[i - 1] == "#")
                    return false;
                else if (words[i] == "#" && words[i - 1] == "*")
                    return false;
                else if (words[i] == "*" && words[i - 1] == "#")
                    return false;
            }
            return true;
        }
路由匹配

route()方法根据不同的交换机类型决定是否匹配成功:

DIRECT类型:路由键和绑定键必须完全相等。

FANOUT类型:无论路由键如何,都会匹配成功。

TOPIC类型:使用动态规划(DP)方法实现复杂的通配符匹配逻辑。
首先将路由键和绑定键按.分隔为多个单词,然后通过DP表逐步匹配各个单词。
如果绑定键包含#*,则会根据通配符的规则处理。

动态规划的应用

DP表的初始化确保了在开始时没有匹配任何字符时(dp[0][0])的正确性。如果绑定键的第一个字符是#,那么也允许匹配空路由键(dp[1][0])。

在填充DP表时,#可以匹配零个或多个单词,而*则只能匹配一个单词。这种设计有效地解决了通配符的匹配问题。

实现总结

Routine类处理了不同类型的消息路由需求,特别是在处理Topic类型的复杂路由规则时,使用了动态规划来高效地完成匹配。

三.全部代码

#pragma once
#include "../common_mq/helper.hpp"
#include "../common_mq/logger.hpp"
#include "../common_mq/msg.pb.h"

namespace mq
{
    class Routine
    {
    public:
        static bool routineKeyValid(const std::string &rkey)
        {
            // 包含的字符规则: 0-9,a-z,A-Z, . _
            for (auto &ch : rkey)
            {
                if (ch >= '0' && ch <= '9' ||
                    ch >= 'a' && ch <= 'z' ||
                    ch >= 'A' && ch <= 'Z' ||
                    ch == '.' || ch == '_')
                    continue;
                else
                    return false;
            }
            return true;
        }
        static bool bindingKeyValid(const std::string &bkey)
        {
            // 1. 包含的字符规则 0-9,a-z,A-Z, . _ * #
            for (auto &ch : bkey)
            {
                if (ch >= '0' && ch <= '9' ||
                    ch >= 'a' && ch <= 'z' ||
                    ch >= 'A' && ch <= 'Z' ||
                    ch == '.' || ch == '_' ||
                    ch == '*' || ch == '#')
                    continue;
                else
                    return false;
            }
            // 2. 单词直接以.作为分隔符,#和* 必须独立作为单词出现
            std::vector<std::string> words;
            StrHelper::split(bkey, ".", words);
            for (auto &word : words)
            {
                if (word.size() > 1 &&
                    (word.find('*') != std::string::npos ||
                     word.find('#') != std::string::npos))
                    return false;
            }
            // 3. #可以匹配0-n个单词,即#前后不能有*和#的通配符
            for (int i = 1; i < words.size(); ++i)
            {
                if (words[i] == "#" && words[i - 1] == "#")
                    return false;
                else if (words[i] == "#" && words[i - 1] == "*")
                    return false;
                else if (words[i] == "*" && words[i - 1] == "#")
                    return false;
            }
            return true;
        }
        // 路由匹配是否成功
        static bool route(msg::ExchangeType type, const std::string &rkey, const std::string &bkey)
        {
            if (type == msg::ExchangeType::DIRECT)
                return rkey == bkey;
            else if (type == msg::ExchangeType::FANOUT)
                return true;
            else//TOPIC
            {
               // 1.字符串分别以.为分隔符切分
                std::vector<std::string> rkeys;
                size_t routine_sz = StrHelper::split(rkey, ".", rkeys);
                std::vector<std::string> bkeys;
                size_t bind_sz = StrHelper::split(bkey, ".", bkeys);
                // 2.创建dp表
                std::vector<std::vector<bool>> dp(bind_sz+1,std::vector<bool>(routine_sz+1,false));
                // 3.初始化dp表
                // dp[0][0]=true
                // binding_key起始字符为0时,dp[1][0]=true
                dp[0][0]=true;
                if(bind_sz>0 && bkeys[0] == "#")
                    dp[1][0] = true;
                // 4.填充dp表
                for(int i=1;i<=bind_sz;i++)
                {
                    for(int j=1;j<=routine_sz;j++)
                    {
                        if(bkeys[i-1] == "#")
                            dp[i][j] = dp[i-1][j-1] | dp[i][j-1] | dp[i-1][j];
                        else if(bkeys[i-1] == "*" || bkeys[i-1] == rkeys[j-1])
                            dp[i][j] = dp[i-1][j-1];
                    }
                }
                // 5.返回最后一个元素
                return dp[bind_sz][routine_sz];
            }
        }
    };
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值