MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议)是用于物联网(IoT)的OASIS标准消息传递协议。发布/订阅是连接远程消息传递设备的理想选择,因为它具有非常小的网络带宽。MQTT目前广泛应用于各种行业,如汽车、制造业、电信、石油和天然气等。
对于MQTT broker,目前主流的实现有EMQ,mosquito,HiveMQ等,但是并没有一个很完整的Go语言实现。目前的开源的Go实现对MQTT协议的支持基本上都是缺胳膊少腿,而Gmqtt完整的实现了MQTT V3.1.1和最新的V5协议,应该是Go语言中对MQTT协议支持最完整的项目。
Gmqtt的诞生是由于之前工作的项目需要,要在MQTT broker里面定制化许多业务逻辑,调研了一些broker都不尽满意,于是乎就撸起袖子自己干,造了这么一个轮子。起初只支持V3.1.1版本,但本着尽善尽美的原则(本人有强迫症),放弃了撸铁时间,肝了一段时间,把V5的特性也全部支持了。
快速开始
跟所有的Go项目一样,go get下载即可。
$ go get -u github.com/DrmagicE/gmqtt
$ cd cmd/gmqttd
$ go run . start -c default_config.yml
2020-12-13T23:11:54.037+0800 INFO server/server.go:996 init plugin hook wrappers
2020-12-13T23:11:54.037+0800 INFO server/server.go:802 open persistence succeeded {"type": "memory"}
2020-12-13T23:11:54.037+0800 INFO server/server.go:825 init session store succeeded {"type": "memory", "session_total": 0}
2020-12-13T23:11:54.037+0800 INFO server/server.go:842 init queue store succeeded {"type": "memory", "session_total": 0}
2020-12-13T23:11:54.037+0800 INFO server/server.go:843 init subscription store succeeded {"type": "memory", "client_total": 0}
2020-12-13T23:11:54.037+0800 INFO server/server.go:1218 loading plugin {"name": "prometheus"}
2020-12-13T23:11:54.037+0800 INFO server/server.go:1218 loading plugin {"name": "admin"}
2020-12-13T23:11:54.038+0800 INFO server/server.go:1259 starting gmqtt server {"tcp server listen on": ["[::]:1883"], "websocket server listen on": [":8883"]}
使用上述的命令将使用默认配置default_config.yml
启动gmqtt,监听1883端口提供TCP服务和8883端口提供websocket服务。Gmqtt默认配置没有启用鉴权,客户端不需配置鉴权可以直接连接。
特点
Gmqtt具备极强的扩展性,你几乎可以通过定制化插件来定制任何逻辑。例如通过HTTP/gRPC接口来查询客户端信息,强制断开连接,订阅主题,发布消息等等。这极强的扩展性得益于gmqtt提供的丰富的钩子函数,以及其内置的扩展接口。
钩子函数
目前,gmqtt提供了17个钩子函数。
hook | 说明 | 用途示例 |
---|---|---|
OnAccept | TCP连接建立时调用 | TCP连接限速,黑白名单等. |
OnStop | Broker退出时调用 | |
OnSubscribe | 收到订阅请求时调用 | 校验订阅是否合法 |
OnSubscribed | 订阅成功后调用 | 统计订阅报文数量 |
OnUnsubscribe | 取消订阅时调用 | 校验是否允许取消订阅 |
OnUnsubscribed | 取消订阅成功后调用 | 统计订阅报文数 |
OnMsgArrived | 收到消息发布报文时调用 | 校验发布权限,改写发布消息 |
OnBasicAuth | 收到连接请求报文时调用 | 客户端连接鉴权 |
OnEnhancedAuth | 收到带有AuthMetho的连接请求报文时调用(V5特性) | 客户端连接鉴权 |
OnReAuth | 收到Auth报文时调用(V5特性) | 客户端连接鉴权 |
OnConnected | 客户端连接成功后调用 | 统计在线客户端数量 |
OnSessionCreated | 客户端创建新session后调用 | 统计session数量 |
OnSessionResumed | 客户端从旧session恢复后调用 | 统计session数量 |
OnSessionTerminated | session删除后调用 | 统计session数量 |
OnDeliver | 消息从broker投递到客户端后调用 | |
OnClosed | 客户端断开连接后调用 | 统计在线客户端数量 |
OnMsgDropped | 消息被丢弃时调用 |
https://github.com/DrmagicE/gmqtt/blob/master/server/hook.go#L11
举其中常用的OnBasicAuth
,OnSubscribe
,OnMsgArrived
为例,说明如何通过这些函数来定制化鉴权逻辑。
我们在内存中保存以下6个客户端的用户名密码。
var validUser = map[string]string{
"root": "pwd", // root用户拥有所有权限
"qos0": "pwd", // qos0用户最高只允许订阅qos0主题
"qos1": "pwd", // qos1用户最高只允许订阅qos1主题
"publishonly": "pwd", // publishonly用户只允许发布,不允许订阅
"subscribeonly": "pwd", // subscribeonly用户只允许订阅,不允许发布
"disable_shared": "pwd", // disable_shared用户禁止订阅表示共享订阅的主题(V5特性)
}
除去以上的针对用户的权限设置外,假设我们由于性能因素的考虑,只允许发布QoS1的消息,忽略所有QoS2消息。
登录鉴权
//authentication
var onBasicAuth server.OnBasicAuth = func(ctx context.Context, client server.Client, req *server.ConnectRequest