在 Moquette 中使用类似 Trie(单词查找树) 的数据结构来实现 Topic 的匹配,本文主要介绍这种数据结构。
Trie
Trie 树,又称单词查找树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。

每一条边或结点可代表一个字母,兄弟结点有相同的公共前缀。在搜索时,从头结点(头结点没有数据)开始,逐个字母进行匹配,从匹配的链路中继续同样的操作,直到所有字母匹配完毕。
CTrie
在 Moquette 中,参考Trie 树,实现了 CTrie 树来进行订阅关系的匹配,如下图便是由 Topic Filter 构成的 CTrie 树:

CTrie 树有以下特点:
- 起始结点为
ROOT结点,该结点不关联数据; - 每一个结点关联一个
Token和一组订阅该Token(确切地说应该是Topic Filter) 的客户端; - 从
ROOT结点到每一个结点都代表了一个Topic Filter; Topic Filter中可以包括通配符,如#,+, 其中#代表零个或多个层级,位于最后一个Token,+代表一个层级,可位于任意位置;- 每一个层级都会关联一组订阅关系,该订阅关系包括如下内容:
TF(Topic Filter),QoS及U(User, 代表一个客户端);
说明:
Topic: 消息主题,每一个消息都会有一个所属的Topic;Topic Filter: 主题过滤器,也可叫主题表达式,每一个客户端订阅感兴趣的Topic时,都需要定义一个Topic Filter,用来匹配或筛选消息;Token:Topic具有层次结构,层级间使用/进行分隔,而一个Token则代表其中一个层级;Topic Filter中可以包含通配符,如#,+,但Topic中不能包含通配符。
实例:
以 sport/tennis/# Topic Filter 为例,它表示匹配 sport/tennis 及 sport/tennis/..下任意层级的 Topic, 其中 sport, tennis, # 代表三个层级或三个 Token.
重点说明:sport/tennis 和 sport/tennis/ 是不一样的 Topic,sport/tennis 其中包括二个层级sport 和 tennis, 而 sport/tennis/ 包括三个层级 sport, tennis 和 "", 空串也是一层级。
匹配规则
在 MQTT 中,订阅关系中的 Topic Filter 会生成一棵 CTrie, MQTT Broker 每当收到一个消息时,便会使用该消息的 Topic 去 CTrie 树中查找订阅该 Topic 的客户端。匹配的本质便是 CTrie 树的查找匹配过程。
任意层级的匹配
可以使用 # 进行多个层级的匹配,如下图所示:

说明:
#可以匹配父级Topic, 如sport/tennis;#可以匹配任意多级Topic, 如:sport/tennis/player1,sport/tennis/player1/score.
单个层级的匹配
可以使用 + 进行单个层级的匹配,如下图所示:

说明:
+不匹配父级Topic;+只匹配一级Token, 如:sport/tennis/,sport/tennis/player1,sport/tennis/player2.