根据源码,模拟实现 RabbitMQ - 转发规则实现(6)

news2024/9/26 3:30:45

目录

一、转发规则实现

1.1、需求分析

1.2、实现 Router 转发规则

1.2.1、bindingKey 和 routingKey 参数校验

1.2.2、消息匹配规则

1.2.3、主题交换机匹配规则


一、转发规则实现


1.1、需求分析

这里主要实现 routingKey 和 bindingKey 参数的校验,以及 TopicExchange 类型绑定规则的实现.

这里重点来看一下 Topic 交换机的转发规则

bindingkey:创建绑定的时候,给绑定指定特殊字符串,相当于出题;

routingKey:发布消息的时候,给消息上指定的特殊字符串,相当于做答案;

当 routingKey 和 bindingKey 匹配(答案对的上),就可以将消息转发给对应的队列。

什么叫做能匹配的上?

routingKey 的组成:

  1. 数字、字母、下划线
  2. 使用 "." 把整个routingKey 分成多个部分

例如:

  • aaa.bbb.ccc        合法
  • aaa.564.bbb       合法
  • aaa                     合法

bindingKey 的组成:

  1. 数字、字母、下划线
  2. 使用 "." 把整个 bindingKey 分成多个部分
  3. 支持两种特殊的符号作为通配符(* 和 # 必须是作为被 . 分割出来的独立部分)
    1. * 可以匹配任何一个独立的部分
    2. # 可以匹配任何 0 个或者多个独立的部分

例如:

  • aaa.*.bbb    合法
  • aaa.*.bbb    非法
  • aaa.#b.ccc  非法

是否能匹配上,有如下几个栗子:


情况一:bindingKey 中没有 * 和 # ,此时,必须要求 routingKey 和 bindingKey 一模一样,才能匹配成功

假设 bindingKey:aaa.bbb.ccc

此时 routingKey 如下:

aaa.bbb.ccc (匹配成功)

aaa.cbb.ccc (匹配失败)


情况二: bindingKey 中有 * 

假设 bindingKey:aaa.*.ccc

此时 routingKey 如下:

aaa.bbb.ccc  (匹配成功)

aaa.gafdga.ccc  (匹配成功)

aaa.bbb.eee.ccc (匹配失败)


情况三:bindingKey 中有 #

假设 bindingKey:aaa.#.ccc

此时 routingKey 如下:

aaa.bbb.ccc   (匹配成功)

aaa.bbb.ddd.ccc   (匹配成功)

aaa.ccc(匹配成功)

aaa.b.ccc.d(匹配失败)


特殊情况:如果把 bindingKey 设置成 #,就可以匹配到所有 routingKey,如下

aaa

aaa.bbb

aaa.bbb.ccc

.......

此时,topic 交换机就相当用户 fanout 交换机了 


Ps:上述规则是 AMQP 协议约定的

1.2、实现 Router 转发规则

1.2.1、bindingKey 和 routingKey 参数校验

bindingKey:

    * 1.数字、字母、下划线
    * 2.使用 . 作为分隔符
    * 3.允许存在 * 和 # 作为通配符,但是通配符只能作为独立的分段

routingKey:

     * 1.字母、数组、下划线
     * 2.使用 . 进行分割

    /**
     * bindingKey 的构造规则
     * @param bindingKey
     * @return
     */
    public boolean checkBindingKey(String bindingKey) {
        if(bindingKey.length() == 0) {
            //空字符串,也是一种合法情况,比如使用 direct(routingKey 直接当作队列名字去匹配) / fanout 交换机的时候,bindingKey 是用不上的
            return true;
        }
        //检查字符串中不能存在的非法字符
        for(int i = 0; i < bindingKey.length(); i++) {
            char ch = bindingKey.charAt(i);
            if(ch >= 'A' && ch <= 'Z') {
                continue;
            }
            if(ch >= 'a' && ch <= 'z') {
                continue;
            }
            if(ch >= '0' && ch <= '9') {
                continue;
            }
            if(ch == '.' || ch == '_' || ch == '*' || ch == '#') {
                continue;
            }
            return false;
        }
        //检查 * 或者 # 是否是独立的部分
        //aaa.*.bbb 是合法的;aaa.a*.bbb 是非法的
        String[] words = bindingKey.split("\\.");//这里是正则表达式
        for(String word : words) {
            //检查 word 长度 > 1,并且包含了 * 或者 # ,就是非法格式了
            if(word.length() > 1 && (word.contains("*") || word.contains("#"))) {
                return false;
            }
        }
        //约定一下,通配符之间的相邻关系(个人约定,不这样约定太难实现)
        //为什么这么约定。因为前三种相邻的时候,实现匹配的逻辑是非常繁琐,同时功能性提升不大
        //1. aaa.#.#.bbb  => 非法
        //2. aaa.#.*.bbb  => 非法
        //3. aaa.*.#.bbb  => 非法
        //4. aaa.*.*.bbb  => 合法
        for(int i = 0; i < words.length - 1; i++) {
            if(words[i].equals("#") && words[i + 1].equals("#")) {
                return false;
            }
            if(words[i].equals("#") && words[i + 1].equals("*")) {
                return false;
            }
            if(words[i].equals("*") && words[i + 1].equals("#")) {
                return false;
            }
        }
        return true;
    }

    /**
     * routingKey 的构造规则
     * @param routingKey
     * @return
     */
    public boolean checkRoutingKey(String routingKey) {
        if(routingKey.length() == 0) {
            //空字符串是合法情况,比如使用 faout 交换机的时候,routingKey 用不上,就可以设置为 “”
            return true;
        }
        for(int i = 0; i < routingKey.length(); i++) {
            char ch = routingKey.charAt(i);
            // 检查大写字母
            if(ch >= 'A' && ch <= 'Z') {
                continue;
            }
            // 检查小写字母
            if(ch >= 'a' && ch <= 'z') {
                continue;
            }
            //检查数字
            if(ch >= '0' && ch <= '9') {
                continue;
            }
            //检查下划线和.
            if(ch == '_' || ch == '.') {
                continue;
            }
            //不是上述规则,就是错误
            return false;
        }
        return true;
    }

Ps:split 方法中的参数是一个正则表达式~  首先 "."  在正则表达式中是一个特殊的符号,"\\." 是把 . 当作原始文本进行匹配,想要使用 . 作为为原始文本,就需要在正则表达式中使用 \. 来表示,又因为,在 Java 的字符串中,"\" 是一个特殊字符,需要使用 "\\" 来转义,此时才能通过 "\\." 的方式真正录入 "." 这个文本

(这个不懂没关系,记住就行了)

1.2.2、消息匹配规则

之前已经具体实现了 直接交换机和扇出交换机的逻辑了,这里主要关注主题交换机.

    /**
     * 判定消息是否可以转发给这个绑定的队列
     * @param exchangeType
     * @param binding
     * @param message
     * @return
     */
    public boolean route(ExchangeType exchangeType, Binding binding, Message message) throws MqException {
        //1.判断当前交换机类型
        if(exchangeType == ExchangeType.FANOUT) {
            // 扇出交换机,要给每一个绑定的队列都发送消息
            return true;
        } else if(exchangeType == ExchangeType.TOPIC) {
            // 主题交换机
            return routeTopic(binding, message);
        } else {
            // 其他情况是不存在的
            throw new MqException("[Router] 交换机类型非法!exchangeType=" + exchangeType);
        }
    }

1.2.3、主题交换机匹配规则

假设 bindingKey:aaa.*.bbb.#.ccc

假设 routingKey:aaa.11.bbb.22.33.ccc

这里我们使用双指针算法进行匹配:

  1. 先将这两个字符串使用 split 通过 "." 进行分割.
  2. 如果指向的是一个普通的字符串,此时要求和 routingKey 的对应下标执行的内容完全一致.、
  3. 如果指向的是 *,此时无论 routingKey 指向的是什么,双方都同时下标前进.
  4. 遇到 # 了,并且如果 # 后面没有其他内容了,直接返回 true,匹配成功.
  5. 遇到 # 了,并且 # 后面还有内容,拿着 # 后面的一段内容,去 routingKey 中查找,找到后面的部分,在 routingKey 中出现的位置(如果后面部分在 routingKey 中不存在,直接认为匹配失败).
  6.  两个指针同时到达末尾,则匹配成功,反之匹配失败

    public boolean routeTopic(Binding binding, Message message) {
        //先进行切分
        String[] bindingTokens = binding.getBindingKey().split("\\.");
        String[] routingTokens = message.getRoutingKey().split("\\.");

        //使用双指针
        int bindingIndex = 0;
        int routingIndex = 0;
        while(bindingIndex < bindingTokens.length && routingIndex < routingTokens.length) {
            if(bindingTokens[bindingIndex].equals("*")) {
                //遇到 * ,继续向后走
                bindingIndex++;
                routingIndex++;
            } else if(bindingTokens[bindingIndex].equals("#")) {
                //如果遇到 #,先看看还有没有下一个位置
                bindingIndex++;
                if(bindingIndex == bindingTokens.length) {
                    //后面没有东西,一定匹配成功
                    return true;
                }
                //如果后面还有东西,就去 routingKey 后面的位置去找
                //findNextMatch 这个方法用来查找该部分再 routingKey 的位置,返回该下标,没找到,就返回 -1
                routingIndex = findNextMatch(routingTokens, routingIndex, bindingTokens[bindingIndex]);
                if(routingIndex == -1) {
                    //后面没有匹配结果,失败
                    return false;
                }
                //找到了,就继续往后匹配
                routingIndex++;
                bindingIndex++;
            } else {
                //如果遇到普通字符串,要求两边的内容是一样的
                if(!bindingTokens[bindingIndex].equals(routingTokens[routingIndex])) {
                    return false;
                }
                bindingIndex++;
                routingIndex++;
            }
        }
        //判定是否双方同时到达末尾
        //比如 aaa.bbb.ccc 和 aaa.bbb 是要匹配失败的
        return (bindingIndex == bindingTokens.length && routingIndex == routingTokens.length);
    }

    private int findNextMatch(String[] routingTokens, int routingIndex, String bindingToken) {
        for(int i = routingIndex; i < routingTokens.length; i++) {
            if(routingTokens[i].equals(bindingToken)) {
                return i;
            }
        }
        return -1;
    }

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/907445.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

邀请函 | 高质量区块链·元宇宙—标准行系列沙龙(北京站)即将开启

区块链、元宇宙是近年来备受关注的新兴技术&#xff0c;也是推动数字经济发展的重要力量。高质量标准引领高质量发展&#xff0c;加快形成标准引领&#xff0c;充分释放区块链、元宇宙对实体经济牵引赋能效应&#xff0c;推进形成相关产业体系高质量发展新格局刻不容缓。 为进…

AIGC|万字长文!带你了解AI大模型技术演进

一、AI的起源 在下面这张图中&#xff0c;我们可以看到两个人物&#xff1a;图中左边的人物是一位演员&#xff0c;他出演了一部名为《模仿游戏》的电影。而这部电影实际上讲述的是图中右边的人物&#xff0c;他就是是人工智能之父图灵&#xff08;Alan Turing&#xff09;。 …

Python可视化在量化交易中的应用(17)_Seaborn核密度曲线图

Seaborn中核密度图的绘制方法 seaborn中绘制核密度图使用的是sns.kdeplot()函数&#xff1a; sns.kdeplot(x,y,shade,vertical,kernel,bw,gridsize200,cut3,clip,legend,cumulative,shade_lowest,cbar,cbar_ax,cbar_kws,ax,weights,hue,palette,hue_order,hue_norm,multiple‘…

做好产品设计应遵循的5大原则

产品设计原则是产品设计需遵循的基础规则&#xff0c;如果没有遵循设计原则&#xff0c;直接进行产品设计&#xff0c;往往会造成产品设计不稳定、冗杂&#xff0c;不能真正满足用户需求&#xff0c;这为项目带来较大风险&#xff0c;影响项目顺利交付。 一般来说&#xff0c;做…

【实用工具】ScreenToGif 制作GIF

1、介绍 官方地址 ScreenToGif是一款优秀的录屏软件&#xff0c;可将屏幕上的软件操作、会议视频、视频聊天等录制为GIF或MP4格式的文件。 ScreenToGif的启动界面包含屏幕录像机、电脑摄像头录制、画板录制和编辑器4个功能。启动录像机功能后&#xff0c;会有一个录制窗口&am…

【Axure高保真原型】通过输入框动态控制折线图

今天和大家分享通过输入框动态控制折线图的原型模板&#xff0c;在输入框里维护项目数据&#xff0c;可以自动生成对应的折线图&#xff0c;鼠标移入对应折点&#xff0c;可以查看对应数据。使用也非常方便&#xff0c;只需要修改输入框里的数据&#xff0c;或者复制粘贴文本&a…

Docker容器与虚拟化技术:Docker-Compose

目录 一、理论 1.Docker-Compose 二、实验 1. Docker Compose 安装部署 2.Docker Compose撰写nginx 镜像 3.Docker Compose撰写tomcat 镜像 三、问题 1.Docker Compose 和 Dockerfile 的区别 四、总结 一、理论 1.Docker-Compose &#xff08;1&#xff09;使用场景…

【Linux】进程间通信之信号机制2

文章目录 信号阻塞代码验证验证信号的阻塞验证信号的阻塞不影响信号注册验证可靠信号不会丢信号&#xff0c;不可靠信号会丢信号验证9号和19号信号不能被阻塞 用信号解决僵尸进程volatile关键字 信号阻塞代码验证 在上篇详解信号机制的博文中&#xff0c;我们提到了设置阻塞位…

RISC-V公测平台发布 · 数据库在RISC-V服务器上的适配评估

前言 上一期讲到YCSB在RISC-V服务器上对MySQL进行性能测试&#xff08;RISC-V公测平台发布 使用YCSB测试SG2042上的MySQL性能&#xff09;&#xff0c;在这一期文章中&#xff0c;我们继续深入讨论RISC-V数据库的应用。本期就继续利用HS-2平台来测试数据库软件在RISC-V服务器…

基于PaddlePaddle实现的声纹识别系统

前言 本项目使用了EcapaTdnn、ResNetSE、ERes2Net、CAM等多种先进的声纹识别模型&#xff0c;不排除以后会支持更多模型&#xff0c;同时本项目也支持了MelSpectrogram、Spectrogram、MFCC、Fbank等多种数据预处理方法&#xff0c;使用了ArcFace Loss&#xff0c;ArcFace loss…

❤echarts折线图完整使用及详细配置参数

❤echarts折线图完整使用及详细配置参数 进入echarts官网 查看案例&#xff0c;下面说说一些echarts图的调节 一、配置echarts具体参数 01 基础版本的折线图 option {xAxis: {type: category,data: [Mon, Tue, Wed, Thu, Fri, Sat, Sun]},yAxis: {type: value},series: [{data…

Charles通过逍遥模拟器抓包APP,亲测可用

1.设置http代理. Proxy-->Proxy settings 2.设置ssl proxy-->ssl proxying settings 3.Charles安装证书 弹出证书安装界面,点击"安装证书" 选择当前用户, 选择: 将所有的证书都放入下列存储: 直接下一步,最后确定提示"导入成功" 4.接着设置Charles安…

年薪47500美元|眼科硕士赴弗吉尼亚大学从事博士后研究

K医生只有医学硕士学位&#xff0c;起初只考虑申请访问学者&#xff0c;最终我们为其争取到“Post-Doctoral Research Associate”&#xff08;博士后研究助理&#xff09;职位&#xff0c;年薪47500美元&#xff0c;大大超出了本人的预期。 K医生背景&#xff1a; 申请类型&a…

提升管班小诀窍

在传统教育中&#xff0c;将考试结果告知家长一直是一项相对麻烦的任务。老师们不得不一个一个的打电话或发短信&#xff0c;耗费大量时间和精力。然而&#xff0c;现在有了易查分&#xff0c;老师们可以轻松地创建自己的成绩查询系统&#xff0c;大大简化了这项任务。 好消息&…

iOS代码混淆

文章目录 一、混淆的原理二、实现混淆1. 创建文件2. 将文件拖导入目录中3. 将以下脚本拷贝到刚新建的confuse.sh文件中4. 修改文件权限5. 修改项目配置6. 添加需要混淆的方法名7. 配置PCH文件8. 运行效果 一、混淆的原理 这里使用的混淆的原理是&#xff0c;用一串随机生成的字…

百华劳保|听厂家聊聊如何检测防水劳保鞋?

说起防水劳保鞋大家可能并不陌生&#xff0c;在有积水或水利工程这些工作场景中使用&#xff0c;是防止水渗透鞋子的安全防护鞋。许多企业会为员工发放防水劳保鞋&#xff0c;在采购时一般都需要进行防水测试&#xff0c;提供相对应的检测报告。今天百华小编与大家聊聊都是如何…

毛利率下滑至负数,但小鹏汽车仍有信心

KlipC报道&#xff1a;8月18日周五&#xff0c;港股盘后美股盘前&#xff0c;小鹏汽车公布截至2023年6月30日的二季度业绩&#xff0c;据数据显示本季度小鹏营收50.6亿人民币&#xff0c;略超预期。但受G3i的存货减值及存货购买合约损失拖累&#xff0c;毛利率下滑至-3.9&#…

vellum (Discovering Houdini VellumⅡ柔体系统)学习笔记

视频地址&#xff1a; https://www.bilibili.com/video/BV1ve411u7nE?p3&spm_id_frompageDriver&vd_source044ee2998086c02fedb124921a28c963&#xff08;搬运&#xff09; 个人笔记如有错误欢迎指正&#xff1b;希望可以节省你的学习时间 ~享受艺术 干杯&#x1f37b…

一“码”当先,PR大征集!2023 和RT-Thread一起赋能开源!

活动地址&#xff1a;https://club.rt-thread.org/ask/article/3c7cf7345ca47a18.html 活动介绍 「一“码”当先&#xff0c;PR大征集&#xff01;」是一项为了鼓励开发者积极参与开源软件开发维护的活动。 你可在Github RT-Thread&#xff08; https://github.com/RT-Thread …