MQTT特点
MQTT协议是为大量计算能力有限,且工作在低带宽、不可靠的网络的远程传感器和控制设备通讯而设计的协议。
1、使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合
2、对负载内容屏蔽的消息传输。
3、使用TCP/IP提供网络连接:
主流的MQTT是基于TCP连接进行数据推送的,但是同样有基于UDP的版本,叫做MQTT-SN。这两种版本由于基于不同的连接方式,优缺点自然也就各有不同了
4、有三种消息发布服务质量:
[1] “至多一次”,消息发布完全依赖底层TCP/IP网络。会发生消息丢失或重复:
这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。这一种方式主要普通APP的推送,倘若你的智能设备在消息推送时未联网,推送过去没收到,再次联网也就收不到了。
[2] “至少一次”,确保消息到达,但消息重复可能会发生:
这一种方式比较鸡肋,在我的想象中没能想到这种质量的发送在常规的APP开发中有什么用处。
[3] “只有一次”,确保消息到达一次:
这一级别可用于如下情况,在计费系统中,消息重复或丢失会导致不正确的结果。这种最高质量的消息发布服务还可以用于即时通讯类的APP的推送,确保用户收到且只会收到一次。
6、使用Last Will和Testament特性通知有关各方客户端异常中断的机制:
Last Will:即遗言机制,用于通知同一主题下的其他设备发送遗言的设备已经断开了连接。
Testament:遗嘱机制,功能类似于Last Will 。
由于MQTT 协议具有开放、简单、轻量、易于实
现等优点, 因此他特别适用于低带宽, 网络不稳定,网络代价昂贵以及处理器和存储器资源有限的嵌入式设备和移动终端上.术语:
客户端(Client):
使用MQTT的程序或设备。客户端总是通过网络连接到服务端。它可以- 发布应用消息给其它相关的客户端。.
- 订阅以请求接受相关的应用消息
- 取消订阅以移除接受应用消息的请求。
- 从服务端断开连接。
服务端(Server):
一个程序或设备,作为发送消息的客户端和请求订阅的客户端之间的中介。服务端
- 接受来自客户端的网络连接
- 接受客户端发布的应用消息
- 处理客户端的订阅和取消订阅请求
- 转发应用消息给符合条件的客户端订阅
订阅(Subscription):
订阅包含一个主题过滤器(Topic Filter)和一个最大的服务质量(QoS)等级。订阅与单个会话(Session)关联。会话可以包含多于一个的订阅。会话的每个订阅都有一个不同的主题过滤器。
主题名(Topic Name):
附加在应用消息上的一个标签,服务端已知且与订阅匹配。服务端发送应用消息的一个副本给每一个匹配的客户端订阅。
主题过滤器(Topic Filter:):
订阅中包含的一个表达式,用于表示相关的一个或多个主题。主题过滤器可以使用通配符。
会话(Session):
客户端和服务端之间的状态交互。一些会话持续时长与网络连接一样,另一些可以在客户端和服务端的多个连续网络连接间扩展。
控制报文(MQTT Control Packet):
通过网络连接发送的信息数据包。MQTT规范定义了十四种不同类型的控制报文,其中一个(PUBLISH报文)用于传输应用消息。
系统框架设计
整个服务器部分主要分成三个层次. 第一层是
MQTT 消息推送broker, 负责完成协议底层的网络通信机制以及针对各种不同类型消息的收发机制; 第二层由身份验证模块、ACL 控制模块、自动订阅模块、
话题统计模块以及状态监控模块组成, 是在底层通信机制的基础上完善整个系统实际应用中所需的各项功能; 第三层是数据存储层, 为第二层的各个模块提供数据的支持, 用于各项数据的统计与交互. 整个系统框架如图1 所示.
各模块的设计与实现
消息推送中间件
目前在各种平台上对于MQTT协议有许多不同的
实现, 这里所选择的Mosquitto 是一款开源的基于C实现的MQTT server/broker, 比较完整的实现了MQTT协议中要求的各项基本功能, 可以在Windows, Linux以及其它类Unix 系统中编译运行.数据存储层
在数据存储层中, 有的数据是需要经常读取的,
比如用户的名称、密码、ID 以及用户间的好友关系等;有的数据是需要经常写入和修改的, 比如某些话题的订阅数; 有的数据是不经常读取或写入的, 比如服务器的运行状态. 为了提高效率, 对于需要经常读取或写入的与用户相关的数据, 用Redis 数据库存储, 对于不经常读取或写入的与服务器状态相关的数据, 用MYSQL 数据库存储.这里的Redis 是一个基于key-value 的开源no-sql
数据库, 它将数据缓存在内存中, 相对于传统的关系型数据库来说, 性能上有很大提高, 特别适用于对访问速度和并发性要求比较高的情况[6,7]. 同时Redis 也支持数据的持久化, 并且支持list, set, hash 等多种不同的数据结构. 在这里采用Redis 来存储每个用户的用户名密码等相关信息和与话题有关的数量统计, 以处理大并发量下的用户访问请求.密码验证模块
在客户端向服务器发起连接请求的时候, 服务器
必须对其进行密码验证, 以决定是否接受该连接请求,验证过程如图2 所示.为了增加数据库的安全性, 在用户注册时需要对其
密码加密后再将其密文存入数据库, 这样即使有人利用非法手段侵入数据库, 也无法得到用户的真实密码. 在这里采用的MD5 加密算法是一种散列加密算法[8], 任何一个密码经过MD5 的HASH 散列计算之后将会产生一个128bit 的序列. 但是缺点是两个相同的密码会产生相同的散列, 为了弥补这个不足, 需要引入SALT 技术[9],在用户注册时生成一个随机数据, 与用户密码一起进行散列计算, 然后将得到的密文和SALT 值一起存入数据库中, 这样就可以保证即使用户的密码相同, 只要随机的SALT 值不同, 其密文就不相同.在用户发起连接请求时, 将用户提交的密码明文与数据库中的SALT 值一起进行散列计算, 将所得密文与数据库中密文进行比对即可对用户密码进行验证.ACL 控制模块
为了规范用户行为, 需要对其话题的订阅和发布
进行权限控制. ACL(Access Control List)又称为访问控制列表, 是一种通过匹配关系对访问权限进行控制,以加强系统安全性的技术[10,11].ACL 表采用的格式为:user <username>[read/write] <topic>或者pattern [read/write] <topic>
第一种格式为特定用户的权限控制规则, 第二种格式为所有用户的权限控制规则, read 表示具有订阅的权限, write 表示具有发布的权限, topic 采用层级结构组织, 检查的时候从左至右各层依次进行匹配.ACL 表赋予用户应该具有的最小权限, 只要满足表中的一项, 即表示验证通过.为了满足移动社交网络中的应用需求, 需要引入通配符来表示用户之间的关系, 在这里, 用%u 表示用户自身的用户名, %c 表示用户自身的ID 号, %f 表示用户的单向关注的关系, %F 表示用户互相关注的关系(这里我们称之为好友), +表示话题中单层的通配, #表示话题中若干层的通配, 一个典型的ACL 表项为:pattern read user/%F/presence/+该规则表示该用户对所有他的好友的presence 之下的一级子话题具有订阅的权限.自动订阅模块
在系统的实际应用中, 用户需要接收各种各样的
推送话题, 例如系统的广播通知、好友的上线提醒、好友发送的即时消息等, 如果每次上线的时候都由客户端来对这些话题进行订阅, 不但会影响客户端的性能,还会占用移动终端的网络资源, 尤其是在用户关系复杂, 需要订阅大量话题的时候. 因此, 服务器可以在用户登录成功之后为用户自动订阅这些话题, 以减少网络上的数据交互. 自动订阅模块流程如图3 所示.
自动订阅支持ACL 控制模块中提到的各项通配
符, 并且将%u 替换为自身用户名, %c 替换为自身ID,含有%f 和%F 的表项会被通配为多个话题分别进行自动订阅. 一个典型的自动订阅表项为:user/%F/presence/+ 1表示每个用户上线时都会自动订阅其好友的presence之下一级的话题, QoS设为1, 中间以空格分隔.
话题统计模块
当大量用户在一段时间内同时订阅某一话题, 或
者向某一话题发布消息时, 意味着这个话题是当前用户所关注的热点. 因此, 对话题的统计有助于分析用户群体的行为方式以及当前社会生活中的热点时事,对于发掘用户的潜在需求有重要的意义.为了提高消息推动服务器的性能, 话题的统计数据记录在redis 数据库中, 以topic-count 的形式存储,当用户在统计的话题上进行订阅或者发布的时候, 服务器调用Redis 的INCR 或DECR 命令对统计数据进行更新.状态监控模块
要确保服务器长时间稳定的运行, 需要定期对系
统运行的状态进行监控. 一方面要监控服务器本身的系统信息, 如CPU 占用率、内存占用率、磁盘剩余空间大小、网络流量等等, 这些数据在linux 环境下可以通过解析/proc 下相关文件的内容来获取; 另一方面需要监控消息推送服务的运行状况, 如活跃的用户数、用户订阅话题的总数等. Mosquitto 提供了对这些信息进行统计的机制, 所得的数据将由服务器以话题的形式定期进行发布, 监控模块需要以客户的身份登录并订阅相应话题以获取相关信息, 采集到的结果插入到MYSQL 数据库中, 以供服务器进行性能分析以预警.