上一篇博客介绍了MQTT服务器的安装,客户端工具的使用,以及MQTT协议常用的一些特性。这篇博客从开发的角度去学习下程序如何接入MQTT数据,如服务器监控数据、消息订阅、数据桥接等。
监控数据
EMQX 提供了管理监控 REST API,这些 API 遵循 OpenAPI (Swagger) 3.0 规范。EMQX 服务启动后,您可以访问 http://localhost:18083/api-docs/index.html 来 查看 API 的文档。
上图所示,操作Swagger API文档需要授权,创建授权的步骤如下:
前提条件,修改默认账号密码:
- 登录Dashboard,默认密码( 默认的访问用户名是
admin
密码是public
)建议您立即修改。登录EMQX 的 HTTP API 的账号密码跟Dashboard登录账号信息一致。 - 使用命令号修改密码
emqx ctl admins passwd admin new-password
创建授权账号
API文档的创建认证方式有两种,操作方式如下
- 在 Dashboard “系统设置” -> “API 密钥” 界面点击创建
- 通过调用API创建 参数如下
curl -u 'admin:123qwe' \
-X 'POST' 'http://localhost:18083/api/v5/api_key' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"name": "EMQX-API-KEY-5",
"expired_at": "2022-12-05T02:01:34.186Z",
"desc": "for testing",
"enable": true,
"expired": true
}' | jq
# | jq 含义: 结果重定向格式化输出
返回秘钥信息
{
"api_key": "eada7c83e86c53b4",
"api_secret": "f2559BifAzriy9C3E9AksNe7sy9CILflK278dHAYw9AYFGhA",
"created_at": "2022-10-16T04:23:18+00:00",
"desc": "for testing",
"enable": true,
"expired": false,
"expired_at": "2022-12-05T02:01:34+00:00",
"name": "EMQX-API-KEY-5"
}
小细节,从官网看到的的调用代码,直接复用的话在我的电脑会报错,原因是多了个符号
API 授权
REST API 认证方式截图
在 Username / Password 处输入api_key / api_secret (之前返回的认证信息) 点击授权,即可
"api_key": "eada7c83e86c53b4",
"api_secret": "f2559BifAzriy9C3E9AksNe7sy9CILflK278dHAYw9AYFGhA",
随便选择一个API接口,点击执行即可,测试效果图如下
应用场景
通过EMQX服务器暴露的REST API, 开发人员可以通过HTTP接口获取EMQX运行信息,如节点信息、运行状态、服务器运行指标、主题信息、设备在线情况等等,其目的是:产品可以根据业务侧重选取一些关键性的业务指标,集成到统一的运维监管平台。
消息订阅
在物联网业务系统中,通过消息订阅的方式获取智能设备实时/定时上报的数据,从而做相应的业务处理,以下Demo 使用golang语言订阅消息。
安装依赖
go get github.com/eclipse/paho.mqtt.golang
普通主题
package main
import (
"fmt"
mqtt "github.com/eclipse/paho.mqtt.golang"
"log"
"os"
"time"
)
var f mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
//fmt.Printf("TOPIC: %s\n", msg.Topic())
//fmt.Printf("MSG: %s\n", msg.Payload())
fmt.Println("--------------------- subscribe message info ", msg.Topic(), string(msg.Payload()))
}
func main() {
mqtt.DEBUG = log.New(os.Stdout, "", 0)
mqtt.ERROR = log.New(os.Stdout, "", 0)
opts := mqtt.NewClientOptions().AddBroker("tcp://localhost:1883").SetClientID("golang_client")
opts.SetKeepAlive(60 * time.Second)
//设置消息回调处理函数
opts.SetDefaultPublishHandler(f)
opts.SetPingTimeout(1 * time.Second)
client := mqtt.NewClient(opts)
if token := client.Connect(); token.Wait() && token.Error() != nil {
panic(token.Error())
}
fmt.Println("--------------------- start to subscribe test Topic")
time.Sleep(10 * time.Second)
if token := client.Subscribe("test", 0, nil); token.Wait() && token.Error() != nil {
fmt.Println(token.Error())
os.Exit(1)
}
// 发布消息
token := client.Publish("hello1", 0, false, "Hello golang")
token.Wait()
time.Sleep(60 * time.Second)
// 取消订阅
if token := client.Unsubscribe("test"); token.Wait() && token.Error() != nil {
fmt.Println(token.Error())
os.Exit(1)
}
// 断开连接
client.Disconnect(250)
time.Sleep(1 * time.Second)
}
执行结果如下,可以看到订阅的消息,发送的消息都有收到。
系统主题订阅
EMQX 周期性发布自身运行状态、消息统计、客户端上下线事件到以 $SYS/
开头系统主题。因此可以通过订阅系统主题消息来获取服务器的状态(跟REST API 主动调用的方式不同),通过消息订阅的模式,系统之间解耦,架构更加优雅。
但是系统主题跟普通主题在安全控制上有所不同,请看以下代码
package main
import (
"fmt"
mqtt "github.com/eclipse/paho.mqtt.golang"
"log"
"os"
"time"
)
var f mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
//fmt.Printf("TOPIC: %s\n", msg.Topic())
//fmt.Printf("MSG: %s\n", msg.Payload())
fmt.Println("--------------------- subscribe message info ", msg.Topic(), string(msg.Payload()))
}
func main() {
mqtt.DEBUG = log.New(os.Stdout, "", 0)
mqtt.ERROR = log.New(os.Stdout, "", 0)
opts := mqtt.NewClientOptions().AddBroker("tcp://localhost:1883").SetClientID("golang_client")
opts.SetKeepAlive(60 * time.Second)
//设置消息回调处理函数
opts.SetDefaultPublishHandler(f)
opts.SetPingTimeout(1 * time.Second)
client := mqtt.NewClient(opts)
if token := client.Connect(); token.Wait() && token.Error() != nil {
panic(token.Error())
}
fmt.Println("--------------------- start to subscribe system Topic")
if token := client.Subscribe("$SYS/brokers/#", 0, nil); token.Wait() && token.Error() != nil {
fmt.Println(token.Error())
os.Exit(1)
}
time.Sleep(1 * time.Hour)
// 取消订阅
if token := client.Unsubscribe("$SYS/brokers/#"); token.Wait() && token.Error() != nil {
fmt.Println(token.Error())
os.Exit(1)
}
// 断开连接
client.Disconnect(250)
time.Sleep(1 * time.Second)
}
运行以上代码,操作MQTTX工具频繁的操作客户端上/下线接收不到任何的数据。究其原因是由于EMQX服务的安全控制机制导致的,EMQX 默认只允许本机的 MQTT 客户端订阅 $SYS 主题(服务器用docker安装,因此跟宿主机不是同一台机器),拒绝其他机器订阅系统主题。
可以通过修改默认规则,来解决此类问题。登录DashBoard, 在 "认证 -> 授权"菜单中,点击设置按钮.默认为deny,改成allow 允许,然后更新配置。
再次运行代码,操作客户端上/下线,查看控制台输出
WebHook
Webhook 是 EMQX 向 HTTP 服务发送消息的通道。通过 Webhook,用户可以选择某个本地主题,将消息 发送到远程 HTTP 服务,也可以将规则的输出发送到 HTTP 服务。
HTTP服务器代码
启动HTTP服务器程序
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"time"
)
func main() {
http.HandleFunc("/webhook", postRequest)
s := &http.Server{
Addr: ":8888",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
err := s.ListenAndServe()
if err != nil {
log.Fatal("listenAndServe: ", err)
}
}
func postRequest(w http.ResponseWriter, r *http.Request) {
var bodyBytes []byte
if r.Body != nil {
bodyBytes, _ = ioutil.ReadAll(r.Body)
}
defer r.Body.Close()
fmt.Println("request args : ", string(bodyBytes))
w.Header().Set("Content-Type", "application/json")
reply := "{\"result\": \"ok\", \"message\": \"success\"}"
w.Write([]byte(reply))
}
创建 Webhook 并关联到规则
- 现在我们访问 Dashboard,选择左边栏 “数据集成” - “数据桥接”:
- 然后点击创建,选择
Webhook
,点击 “下一步”:
- 将 Webhook 命名为
my_webhook
,URL 为http://192.168.0.17:8888/webhook
(docker安装EMQX 不能填写localhost):
- 点击 “创建”,然后在弹出来的对话框里选择创建关联规则:
- 在规则的创建页面,填入如下 SQL 语句,其余参数保持默认值:
SELECT * FROM "tt/#"
点击页面下方的 “创建” 按钮。
发送数据进行测试
接下来我们使用 MQTTX 发送一条数据到 tt/2`
性能测试
emqtt_bench
是基于 Erlang 编写的,一个简洁强大的 MQTT 协议性能测试工具。在安装该工具时,来来回回折腾了近2小时,到最后无赖放弃。
官方说明,只需要21.2以上的版本即可,实际测试发现在执行make命令编译的时候发现以下两个版本根本压根不行:
- Erlang/OTP 21
- Erlang/OTP 25
最后下载 Erlang/OTP 23.3.4.18 该版本才能正常执行make命令,但是事情尚未结束
[root@localhost emqtt-bench]# make
/opt/software/mqtt/emqtt-bench/rebar3 unlock
/opt/software/mqtt/emqtt-bench/rebar3 compile
===> Verifying dependencies...
===> Fetching quicer (from {git,"https://github.com/emqx/quic.git",{tag,"0.0.16"}})
===> Failed to fetch and copy dep: {git,"https://github.com/emqx/quic.git",
{tag,"0.0.16"}}
make: *** [compile] Error 1
最后无奈放弃,等有时间再去研究。
Jmeter
插件安装
将jar包放入到 $JMETER_HOME/lib/ext 目录下,启动Jmeter,在取样器里面看到MQTT相关的信息代表安装成功
数据测试
Jmeter MQTT插件提供4个维度的取样器进行MQTT测试
- 连接测试
- 断开链接
- 消息发送
- 消息订阅
下面测试连接、消息发送两种维度(插件目前只支持MQTT3的协议,MQTT5协议暂不支持)
选择随机发送数据,点击测试然后观察EMQX Dashboard,可以明显得看到MQTT服务器的连接数、消息流入信息。
Jmeter 的聚合报告如下