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

目录

一、转发规则实现

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 的位置,返回该下标,没找到,就返回 -1routingIndex = 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.mzph.cn/news/54584.shtml

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

相关文章

Python(.pyc)反编译:pycdc工具安装与使用

本文将介绍如何将python的.pyc文件反编译成源码&#xff0c;以便我们对源码的学习与改进。pycdc工具安装 下载地址&#xff1a; 1、Github地址&#xff1a;https://github.com/zrax/pycdc &#xff0c;下载后需要使用CMake进行编译。 2、已下载好及编译好的地址&#xff1a;ht…

ISIS路由协议

骨干区域与非骨干区域 凡是由级别2组建起来的邻居形成骨干区域&#xff1b;级别1就在非骨干区域&#xff0c;骨干区域有且只有一个&#xff0c;并且需要连续&#xff0c;ISIS在IP环境下目前不支持虚链路。 路由器级别 L1路由器只能建立L1的邻居&#xff1b;L2路由器只能建立L…

1.2 Kali Linux的网络配置

前言 最新文章请见此处&#xff0c;持续更新&#xff0c;敬请订阅&#xff01;https://blog.csdn.net/algorithmyyds/category_12418682.html 网络在如今的社会已是十分重要的媒介&#xff0c;如果没有网络&#xff0c;很多事情将难以办成。渗透测试也是一样——毕竟在攻击机…

学习设计模式之享元模式,但是宝可梦

前言 作者在准备秋招中&#xff0c;学习设计模式&#xff0c;做点小笔记&#xff0c;用宝可梦为场景举例&#xff0c;有错误欢迎指出。 享元模式 1 介绍 享元模式很好理解&#xff0c;它主要是为了减少创建对象的数量&#xff0c;属于结构型设计模式 目的&#xff1a;减少…

Jacoco XML 解析

1 XML解析器对比 1. DOM解析器&#xff1a; ○ 优点&#xff1a;易于使用&#xff0c;提供完整的文档树&#xff0c;可以方便地修改和遍历XML文档。 ○ 缺点&#xff1a;对大型文档消耗内存较多&#xff0c;加载整个文档可能会变慢。 ○ 适用场景&#xff1a;适合小型XML文档…

函数式编程-Stream流学习第二节-中间操作

1 Stream流概述 java8使用的是函数式编程模式,如同它的名字一样&#xff0c;它可以用来对集合或者数组进行链状流式操作&#xff0c;让我们更方便的对集合或者数组进行操作。 2 案例准备工作 我们首先创建2个类一个作家类&#xff0c;一个图书类 package com.stream.model;…

03.sqlite3学习——数据类型

目录 sqlite3学习——数据类型 SQL语句的功能 SQL语法 SQL命令 SQL数据类型 数字类型 整型 浮点型 定点型decimal 浮点型 VS decimal 日期类型 字符串类型 CHAR和VARCHAR BLOB和TEXT SQLite 数据类型 SQLite 存储类 SQLite 亲和类型(Affinity)及类型名称 Boo…

opencv案例03 -基于OpenCV实现二维码生成,发现,定位,识别

1.二维码的生成 废话不多说&#xff0c;直接上代码 # 生成二维码 import qrcode# 二维码包含的示例数据 data "B0018" # 生成的二维码图片名称 filename "qrcode.png" # 生成二维码 img qrcode.make(data) # 保存成图片输出 img.save(filename)img.sh…

vue关闭弹窗刷新父页面 this.$refs

代码截图 主页面 弹出框页面 接这一篇文章后续 参考链接

Python 数据分析——matplotlib 快速绘图

matplotlib采用面向对象的技术来实现&#xff0c;因此组成图表的各个元素都是对象&#xff0c;在编写较大的应用程序时通过面向对象的方式使用matplotlib将更加有效。但是使用这种面向对象的调用接口进行绘图比较烦琐&#xff0c;因此matplotlib还提供了快速绘图的pyplot模块。…

Python迭代器和生成器

一、迭代器 1.1、什么是迭代 迭代是指反复的执行一个过程&#xff0c;每次执行都会根据前一次的结果进行调整和优化。在计算机编程中&#xff0c;迭代常常用于循环执行某个操作&#xff0c;直到达到特定的条件或达成目标。迭代也可以用于指代软件开发中的“迭代开发”&#x…

R语言主成分分析

R语言主成分分析 之前介绍过怎么用SPSS进行主成分分析(PCA)&#xff0c;已经忘了的朋友们可以到主页看看 今天主要介绍下R语言主成分分析的几种方法。都是入门级别&#xff0c;跟着我一步步走&#xff0c;一点都不难哈~ 首先调用R语言自带的数据集&#xff0c;USArrests。这…

【Go 基础篇】Go语言中的数组:初识与应用

Go语言以其简洁、高效和强大的特性在编程界广受欢迎。数组作为一种基本的数据结构&#xff0c;在各种应用场景中扮演着重要角色。本文将引入Go语言中的数组&#xff0c;介绍其特点、创建、初始化以及基本应用&#xff0c;为你打开数组的大门。 前言 数组是一种固定大小的数据…

七层、四层和五层网络模型区别和联系

七层、四层和五层网络模型区别和联系 概述OSI网络7层模型&#xff08;概念型框架&#xff09;概述图片分析 四层模型概述常用协议OSI与TCP/IP四层的区别 五层模型概述三种网络模型对比 总结 概述 网络模型-七层模型&#xff08;OSI模型&#xff09;、五层协议体系结构和TCP/IP…

性能优化之分库分表

1、什么是分库分表 1.1、分表 将同一个库中的一张表&#xff08;比如SPU表&#xff09;按某种方式&#xff08;垂直拆分、水平拆分&#xff09;拆分成SPU1、SPU2、SPU3、SPU4…等若干张表&#xff0c;如下图所示&#xff1a; 1.2、分库 在表数据不变的情况下&#xff0c;对…

“R语言+遥感“水环境综合评价方法

详情点击链接&#xff1a;"R语言遥感"水环境综合评价方法 一&#xff1a;R语言 1.1 R语言特点&#xff08;R语言&#xff09; 1.2 安装R&#xff08;R语言&#xff09; 1.3 安装RStudio&#xff08;R语言&#xff09; &#xff08;1&#xff09;下载地址 &…

如何基于亚马逊云科技打造高性能的 SQL 向量数据库 MyScale

MyScale 是一款完全托管于亚马逊云科技、支持 SQL 的高效向量数据库。MyScale 的优势在于&#xff0c;它在提供与专用向量数据库相匹敌甚至优于的性能的同时&#xff0c;还支持完整的 SQL 语法。在这篇文章中&#xff0c;我们将阐述 MyScale 是如何借助亚马逊云科技的基础设施&…

pnpm无法加载文件 (解决方法 )

现在要运行一个TS的项目&#xff0c;我的电脑上没有安装pnpm&#xff0c;导致我的vscode一直报错无法加载。 pnpm安装&#xff1a; npm install -g pnpm pnpm : 无法加载文件 pnpm : 无法加载文件 C:\Users\HP\AppData\Roaming\npm\pnpm.ps1&#xff0c;因为在此系统上禁止运…

英语略读三

课文的客观&#xff0c;或者逻辑推理 同增通减 比错 对比选项&#xff0c;找一个明显的区别 防并列&#xff0c;文章再说主语在干嘛干嘛的&#xff0c;但是与答案的角度不一样&#xff0c;是并列的关系 在对比选项&#xff0c;不是证明正确的 具体问题具体分析&#xff0c;but…

【30天熟悉Go语言】10 Go异常处理机制

作者&#xff1a;秃秃爱健身&#xff0c;多平台博客专家&#xff0c;某大厂后端开发&#xff0c;个人IP起于源码分析文章 &#x1f60b;。 源码系列专栏&#xff1a;Spring MVC源码系列、Spring Boot源码系列、SpringCloud源码系列&#xff08;含&#xff1a;Ribbon、Feign&…