订阅树的概念
Mosquitto经过订阅树的方式来管理全部的topic以及客户端的订阅关系,它首先将全部的topic按照/分割并组织成一棵树结构,从根节点到树中的每一个节点即组成该节点所对应的一个topic,每一个topic都保存一个订阅列表,该订阅列表中保存了全部订阅当前topic的客户端信息。例若有以下订阅关系:
客户端a1,a2,a3订阅了topic:A1/B1/C1m
客户端b1,b2订阅了topic:A2/B2/C2
客户端c1,c2订阅了topic:A1/B1/C3
客户端d1订阅了topic:A2/B3
则上述订阅树如图。函数
Mosquitto程序在实现中根据topic消息的性质将订阅树分为两颗子树:业务子树和系统子树;mosquitto程序中将topic分为两种类型来处理:系统topic和业务topic,前者主要用于发布和维护mosquitto内部的系统消息,后者的topic是用户订阅的业务topic,作这种区分的缘由是由于这两种的类型的topic性质和实现方式上有许多差异,这种差异主要体如今如下3点:
1)生存周期不一样,系统topic不管是否有用户订阅都会存在与订阅树中,而业务topic必须有客户端订阅才能存在(除非其消息字段retain设置为1)。
2)建立方式不一样,系统topic在消息发布时进行建立,业务topic便可以在订阅时建立也能够在消息发布时建立(此时须要该消息retain字段设置为1)。
3)消息保存方式不一样,凡是发布到系统topic的消息都会被保存下来,业务消息将直接挂到订阅列表的各context的消息队列中,若是没有链接订阅或未设置其retain字段,消息将不会被保存下来,消息的retain字段是否被设置在函数mqtt3_handle_publish进行检查。ui
订阅树的建立:(在src/database.c中的mqtt3_db_open函数实现)
mosquitto程序启动时将建立订阅树,该过程将建立三个节点:订阅树总根节点、业务子树根节点和系统子树根节点,这两个子树根节点做为订阅树总根节点的两个子节点,其中订阅树总根节点和业务子树的根节点中topic成员的值为空字符串,而系统子树根节点中保存的值为“$SYS”,如图: spa
搭建订阅树
1) 系统子树搭建过程
Mosquitto中,系统子树在发布系统消息时,自动检测topic片断是否存在,若是不存在则在系统topic上建立节点以搭建订阅树。搭建过程以下:
将Topic按照“/"分红 topic片断;根据第一个topic片断“$SYS”遍历订阅树的子节点找到系统子树的根节点;根据topic下一个片断查找系统子树,若没有则建立这个节点,依次方案处理直至topic片断解析完。
所用到的函数调用: mqtt3_db_messages_easy_queue(在src/database.c中) --->mqtt3_db_messages_queue (在src/subs.c中) ---> _sub_add(在src/subs.c中)server
2)业务子树搭建过程
分为两种类型:订阅时建立和消息发布时建立。后者与系统Topic的方式相似。前者在收到订阅请求后将该客户端挂到对应的业务子树节点的订阅列表中,若不存在客户端所订阅的Topic,则会自动为之添加相应节点。
所用到的函数调用: mqtt3_handle_subscribe(在src/read_handle_server.c中) --->mqtt3_sub_add(在src/subs.c中) --->_sub_add(在src/subs.c中)token
能够看到,在上面都使用了_sub_add函数,而调用它的分别是mqtt3_db_messages_queue 和mqtt3_sub_add函数,并且这三个函数都是在src/subs.c中,不妨来看看它们的逻辑。
mqtt3_db_messages_queue:(系统子树的搭建)
队列