What is MQTT?
MQTT is a lightweight publish/subscribe messaging protocol designed for M2M (machine to machine) telemetry in low bandwidth environments. It was designed by Andy Stanford-Clark (IBM) and Arlen Nipper in 1999 for connecting Oil Pipeline telemetry systems over satellite. Although it started as a proprietary protocol it was released Royalty free in 2010 and became an OASIS standard in 2014. MQTT stands for MQ Telemetry Transport but previously was known as Message Queuing Telemetry Transport. MQTT is fast becoming one of the main protocols for IOT (internet of things) deployments.
简单说一下:MQTT 是一种轻量级发布/订阅消息传递协议,专为低带宽环境中的 M2M(机器对机器)遥测而设计。
MQTT 协议
MQTT is an OASIS standard messaging protocol for the Internet of Things (IoT).
It is designed as an extremely lightweight publish/subscribe messaging transport that is ideal for connecting remote devices
with a small code footprint and minimal network bandwidth. MQTT today is used in a wide variety of industries,
such as automotive, manufacturing, telecommunications, oil and gas, etc.
这是 MQTT 协议的官方描述,它是一种应用于物联网的轻量级的发布订阅协议,类似于 AMQP。详细了解可以参考:
- MQTT Specifications
- [emqx mqtt 协议介绍](docs.emqx.cn/broker/v4.3… 协议)
- 消息推送标准协议:MQTT
通信方式
默认是发布 / 订阅模式的。
- 通信系统中有发布者和订阅者。发布者发布消息而订阅者接收消息。我们把发布者和订阅者统称为客户端。客户端可以同时是发布者和订阅者。
- 在系统中有另外一个角色,它接收发布者的消息并且将消息派发给订阅者。我们一般称这个角色为消息 Broker。
- 在 MQTT 中默认是广播的,也就是说订阅了相同 topic 的订阅者都能收到发布者发送的消息。
基于主题 (Topic) 消息路由
MQTT 协议基于主题 (Topic) 进行消息路由,主题 (Topic) 类似 URL 路径,例如:
chat/room/1 sensor/10/temperature sensor/+/temperature
sensor/#
主题 (Topic) 通过'/'分割层级,支持'+', '#'通配符:
- '+': 表示通配一个层级,例如 a/+,匹配 a/x, a/y
- '#': 表示通配多个层级,例如 a/#,匹配 a/x, a/b/c/d
- 订阅者可以订阅含通配符主题,但发布者不允许向含通配符主题发布消息。
QoS
为了满足不同的场景,MQTT 支持三种不同级别的服务质量(Quality of Service,QoS)为不同场景提供消息可靠性:
- 0:At most once。消息发送者会想尽办法发送消息,但是遇到意外并不会重试。
- 1:At least once。消息接收者如果没有知会或者知会本身丢失,消息发送者会再次发送以保证消息接收者至少会收到一次,当然可能造成重复消息。
- 2:Exactly onces。保证这种语义肯待会减少并发或者增加延时,不过丢失或者重复消息是不可接受的时候,级别 2 是最合适的。
订阅者收到 MQTT 消息的 QoS 级别,最终取决于发布消息的 QoS 和主题订阅的 QoS。
Broker 选型
本文使用的 MQTT Broker 是 EMQ X 的开源版,官方API
EMQ X (Erlang/Enterprise/Elastic MQTT Broker) 是基于 Erlang/OTP 平台开发的开源物联网 MQTT 消息服务器。 Erlang/OTP 是出色的软实时 (Soft-Realtime)、低延时 (Low-Latency)、分布式 (Distributed) 的语言平台。 EMQ X 设计目标是实现高可靠,并支持承载海量物联网终端的MQTT连接,支持在海量物联网设备间低延时消息路由: 1、稳定承载大规模的 MQTT 客户端连接,单服务器节点支持50万到100万连接。 2、分布式节点集群,快速低延时的消息路由,单集群支持1000万规模的路由。 3、消息服务器内扩展,支持定制多种认证方式、高效存储消息到后端数据库。 4、完整物联网协议支持,MQTT、MQTT-SN、CoAP、LwM2M、WebSocket 或私有协议支持。
部署方案
部署采用的是目前主流的容器化方案--docker(如对于docker不熟悉,后期将出一篇博客),同时官方也提供了docker支持
docker pull emqx/emqx:latest //2021-12月最新版为:4.3.8
docker-compose配置
services:
mqttBase:
image: 私有镜像中心地址/public/emqx:v4.3.8
container_name: mqtt_base
restart: always
ports:
- 41581:1883
- 28183:18083
volumes:
- /etc/localtime:/etc/localtime
environment:
- EMQX_LOADED_PLUGINS="emqx_management,emqx_dashboard,emqx_web_hook"
- EMQX_LISTENER__TCP__EXTERNAL__MAX_CONN_RATE=10000
- EMQX_WEB__HOOK__API__URL=http://wedoraMqttMonitor:9040/hook
- AUTH__HTTP__AUTH_REQ__URL=http://wedoraMqttMonitor:9040/mqttAuth/acl
mqtt_auth:
image: 私有镜像中心地址/public/emqx-auth:V4.3.8.1
container_name: mqtt_auth
restart: always
ports:
- 41586:1883
- 41587:8883
- 28182:18083
volumes:
- /etc/localtime:/etc/localtime
environment:
- EMQX_LOADED_PLUGINS="emqx_management,emqx_dashboard,emqx_web_hook,emqx_auth_redis"
- EMQX_WEB__HOOK__API__URL=http://wedoraMqttMonitor:9040/hook
接下来主要围绕配置文件作讲解,
仔细的朋友应该看出两个服务的配置有明显差异:
1、两个服务的版本不一样
2、mqttBase仅暴露了两个内部端口,mqttAuth暴露了三个内部端口
3、mqttBase开启了acl,mqttAuth开启了auth。(其实开启auth会默认开启acl)
之所以有这些差异,主要是业务需求,不同安全级别的设备可以根据需求接到不同的服务上。
看过上面EMQX官方文档的朋友就知道emqx默认开启了两个连接端口1883和8883以及一个监控平台端口18083。1883端口为普通连接端口,8883端口为ssl连接端口(类似http-80,https-443)
另外mqttAuth开启了emqx_auth_redis插件,这个插件是emqx认证的一种,使用的是外部数据源-redis,将用户信息存储在redis内,格式官方文档也有介绍。
下面主要介绍的是ssl认证的,上面已经介绍了emqx默认开启两个内部端口,其中的8883就是专门给TLS/SSL 使用的。
了解ssl的同学应该都知道ssl分为单向认证和双向认证,我使用的是单向认证,为什么?因为相对双向认证方便,就是这么任性,同时也保证了连接的安全性。
证书的生成这里就忽略了,想了解的同学可参考EMQ X MQTT 服务器启用 SSL/TLS 安全连接
另外,我对mqttAuth进行了定制化开发,将证书及redis插件配置直接写到容器内。同学们可能会疑惑,写到容器内,如果我更换环境redis配置怎么改呢?放心docker内部是可以使用容器名称:端口进行交互的,只要你的redis容器名称不变,mqttAuth内的redis配置连接就不用变更。
emqx_auth_redis配置
# etc/plugins/emqx_auth_redis.conf
## 服务器地址
auth.redis.server =redis:6379
## 连接池大小
auth.redis.pool = 8
auth.redis.database = 0
auth.redis.password =
等什么时候有时间了,我在将emqx acl规则也给大家简单介绍一下。