本文由逍遥子撰写,转发请标注原址:

http://write.blog.csdn.net/postedit/21462255

一、  Mosquito的核心功能分析

3.1、订阅树

Mosquitto通过订阅树的方式来管理所有的topic以及客户端的订阅关系,它首先将所有的topic按照/分割并组织成一棵树结构,从根节点到树中的每个节点即组成该节点所对应的一个topic,每个topic都保存一个订阅列表,该订阅列表中保存了所有订阅当前topic的客户端信息。例如有如下订阅关系:

客户端a1,a2,a3订阅了topic:A1/B1/C1

客户端b1,b2订阅了topic:A1/B1/C2

客户端c1订阅了topic:A2/B2

客户端d1,d2订阅了topic:A2/B3/C3

则在mosquitto程序中需要先将topic按照/进行分割,然后将分割后的topic片段组织成订阅树,上述订阅树的示意图为图3-1:

图3-1          订阅树示意图

Mosquitto程序在实现中根据topic消息的性质将订阅树分为两颗子树:业务子树和系统子树;mosquitto程序中将topic分为两种类型来处理:系统topic和业务topic,前者主要用于发布和维护mosquitto内部的系统消息,后者的topic是用户订阅的业务topic,做这种区分的原因是因为这两种的类型的topic性质和实现方式上有许多差别,这种差别主要体现在以下4点:

1)生存周期不同,系统topic无论是否有用户订阅都会存在与订阅树中,而业务topic必须有客户端订阅才能存在(除非其消息字段retain设置为1);

2)创建方式不同,系统topic在消息发布时进行创建,业务topic即可以在订阅时创建也可以在消息发布时创建(此时需要该消息retain字段设置为1);

3)消息保存方式不同,凡是发布到系统topic的消息都会被保存下来,业务消息将直接挂到订阅列表的各context的消息队列中,如果没有连接订阅或未设置其retain字段,消息将不会被保存下来;

消息的retain字段是否被设置在函数mqtt3_handle_publish进行检查,在该函数中有如下代码:

retain = (header & 0x01);

该代码可获取消息头部的第一个bit位,在mqtt3.1协议中,该为用于表示消息的类型是否为retain。

订阅树在程序中的采用孩子—兄弟链表法来表示。其主要涉及的数据结构是:

struct _mosquitto_subhier

struct _mosquitto_subleaf

3.1.1、订阅树的搭建

1、创建订阅树

mosquitto程序启动时将创建订阅树,该过程将创建三个节点:订阅树总根节点、业务子树根节点和系统子树根节点,这两个子树根节点作为订阅树总根节点的两个子节点,其中订阅树总根节点和业务子树的根节点中topic成员的值为空字符串,而系统子树根节点中保存的值为“$SYS”,如图3-2所示。

图3-2          订阅树的创建

订阅树的创建主要在文件database.c中mqtt3_db_open函数里实现。订阅树中节点的数据结构为struct _mosquitto_subhier,订阅树采用“孩子—兄弟”链表法保存。

2、搭建订阅树

在订阅树中,系统子树与业务子树的搭建过程不一样,系统子树是在系统消息发布时创建,而业务子树创建过程即可以在消息发布时创建也可以在客户端订阅时才创建。

1)系统子树搭建过程

Mosquitto中,系统子树在发布系统消息时,自动检测topic片段是否存在,如果不存在则在系统topic上创建节点以搭建订阅树。例如,mosquitto程序启动时,将首先向系统topic:$SYS/broker/version发送一条版本消息“mosquittoversion 1.2”,此时订阅树的系统子树只有一个根节点,如图3.2所示,其搭建过程如下:

(1)将topic按照”/”分成topic片段,系统Topic:$SYS/broker/version将被分割为$SYS、broker、version三部分。

(2)根据第一个topic片段“$SYS”遍历订阅树的子节点找到系统子树的根节点。

(3)根据topic下一个片段“broker”查找系统子树;此时系统子树中不存在topic片段“broker”的节点,则为订阅树产生一个节点,其数据结构为:struct_mosquitto_subhier。此时订阅树由图3-2变为图3-3所示:

图3-3添加broker节点之后的订阅树

上述过程在函数mqtt3_db_messages_queue中调用函数_sub_add来完成。

(4)依此处理topic剩下的片段,在系统子树中添加topic片段“version”,该过程通过递归调用函数_sub_add来完成。添加完“version”片段之后的订阅树如图3-4所示。

图3-4添加version之后的订阅树

系统子树搭建过程中,所用到的函数调用关系如下图3-5所示

图3-5 系统子树搭建的函数调用关系

2) 业务子树搭建过程

业务子树的搭建分为两种类型:一种在订阅时创建;一种是在消息发布时创建,这种方式与系统topic的创建过程一样,因此下面的内容将主要描述前一种方式。

在mosquitto程序运行期间,收到一条客户端的订阅请求后将调用函数mqtt3_sub_add将该客户端挂到对应的业务子树节点的订阅列表中,此时,如果所订阅树中不存在客户端所订阅的topic,则会自动为之添加相应的节点,此过程即为订阅树的业务子树搭建过程。例如,在mosquitto程序启动时(此时订阅树如图3-2所示)客户端订阅了topic:year/month/events,业务子树的搭建过程为:

(1)将topic按照”/”分成topic片段,topic:year/month/event将被分割成:year、month、events三个topic片段;此过程在函数mqtt3_sub_add中完成。

(2)遍历订阅树的第一级子节点,以查找业务子树的根节点,找到业务子树的根节点之后进行后续的处理。此过程在函数mqtt3_sub_add中完成。

(3)依此处理三级topic片段:year、month、events,此过程与系统topic的添加过程相似,如果订阅树中不某个存在topic片段,则为订阅树添加此节点,添加完成之后的订阅树如图3-6:本条功能在函数_sub_add中完成。

图3-6 添加业务topic之后的订阅树

业务子树搭建过程中,所用到的函数调用关系如下图3-7所示

图3-7 业务子树搭建的函数调用关系

mosquitto源码分析(三)相关推荐

  1. mosquitto源码分析

    mosquitto源码分析 一.mosquitto简介 二.主要目录 三.客户端源码 结构体 重要函数 1.mosquitto_lib_init 2.mosquitto_new 3.mosquitto ...

  2. Nouveau源码分析(三):NVIDIA设备初始化之nouveau_drm_probe

    Nouveau源码分析(三) 向DRM注册了Nouveau驱动之后,内核中的PCI模块就会扫描所有没有对应驱动的设备,然后和nouveau_drm_pci_table对照. 对于匹配的设备,PCI模块 ...

  3. 【投屏】Scrcpy源码分析三(Client篇-投屏阶段)

    Scrcpy源码分析系列 [投屏]Scrcpy源码分析一(编译篇) [投屏]Scrcpy源码分析二(Client篇-连接阶段) [投屏]Scrcpy源码分析三(Client篇-投屏阶段) [投屏]Sc ...

  4. Spring源码分析(三)

    Spring源码分析 第三章 手写Ioc和Aop 文章目录 Spring源码分析 前言 一.模拟业务场景 (一) 功能介绍 (二) 关键功能代码 (三) 问题分析 二.使用ioc和aop重构 (一) ...

  5. ABP源码分析三十四:ABP.Web.Mvc

    ABP.Web.Mvc模块主要完成两个任务: 第一,通过自定义的AbpController抽象基类封装ABP核心模块中的功能,以便利的方式提供给我们创建controller使用. 第二,一些常见的基础 ...

  6. 【转】ABP源码分析三十五:ABP中动态WebAPI原理解析

    动态WebAPI应该算是ABP中最Magic的功能之一了吧.开发人员无须定义继承自ApiController的类,只须重用Application Service中的类就可以对外提供WebAPI的功能, ...

  7. 【转】ABP源码分析三十四:ABP.Web.Mvc

    ABP.Web.Mvc模块主要完成两个任务: 第一,通过自定义的AbpController抽象基类封装ABP核心模块中的功能,以便利的方式提供给我们创建controller使用. 第二,一些常见的基础 ...

  8. 【转】ABP源码分析三十三:ABP.Web

    ABP.Web模块并不复杂,主要完成ABP系统的初始化和一些基础功能的实现. AbpWebApplication : 继承自ASP.Net的HttpApplication类,主要完成下面三件事 一,在 ...

  9. 【转】ABP源码分析三十一:ABP.AutoMapper

    这个模块封装了Automapper,使其更易于使用. 下图描述了改模块涉及的所有类之间的关系. AutoMapAttribute,AutoMapFromAttribute和AutoMapToAttri ...

  10. 【转】ABP源码分析三:ABP Module

    Abp是基于模块化设计思想进行构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modul ...

最新文章

  1. 一款让你轻松在IDEA画图的插件!
  2. AC日记——凌乱的yyy 洛谷 P1803
  3. 中石油训练赛 - Swapping Places(字典序最小的拓扑排序)
  4. android gdb 远程调试工具,Android下用gdb远程调试办法
  5. 启迪公交:DRDS助力城市公交系统智能化
  6. python写的贪吃蛇小游戏_Python贪吃蛇小游戏
  7. Python交互模式方向键出现乱码
  8. linux gnome_GNOME,生日快乐:喜欢这个Linux桌面的8个理由
  9. plsql设置oracle11g,数据库oracle11G,如何通过plsql查询表空间大小,如何通过PLSQL把一个表空间的大小设置成自动扩展...
  10. android ios 传视频播放器,推荐用于iOS / Android全面格式的视频播放器|手机最强大的播放器...
  11. SQL数据库的多表查询
  12. Hive误删除后,如何恢复数据
  13. 微信小程序:热门表情包+头像+壁纸自动采集多分类微信小程序
  14. Thinking in React(翻译)
  15. 分析PNG图片格式——数据块
  16. 关于深度学习,这可能是你最容易读懂的科普贴
  17. python 因子分析 权重计算方法_因子得分如何计算_spss如何计算因子得分
  18. android系统日志如何查看,Android如何查看系统recovery日志,从而找到系统程序、刷机异常…...
  19. 艺术品区块链溯源防伪平台(连载三)数字资产如何上链
  20. 表达式左值右值(C++学习)

热门文章

  1. Python面向对象编程三大特性之多态
  2. C++中cin的常用用法
  3. python requests返回值为200 但是text无内容_接口测试入门神器 - Requests
  4. 用python实现单例模式_Python单例模式的两种实现方式
  5. JAVA获取图片的宽、高和大小
  6. 在Windows上编译Spark源码
  7. 判断并输出三个数abc 中的最大值 (C语言)
  8. 中文linux最小,35M的中文linux硬盘简单安装方法Live-CD:SliTaz.tw-全世界最小的li
  9. Java流程控制01 用户交互Scanner
  10. 新计算机主板不亮,电脑组装完毕之后点不亮怎么办 新电脑组装好开不了机的解决办法...