mqttClient.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. package mqttClient
  2. import (
  3. "crypto/tls"
  4. "fmt"
  5. "time"
  6. mqtt "github.com/eclipse/paho.mqtt.golang"
  7. "go.uber.org/zap"
  8. )
  9. type MqttClientOpt struct {
  10. Broker string // 服务端地址
  11. ClientID string // (可选) 客户端名称
  12. TLS *tls.Config
  13. }
  14. type MqttClient struct {
  15. client mqtt.Client
  16. subscribeMap map[string]func(payload []byte)
  17. clientID string // 客户端名称
  18. }
  19. const (
  20. connectTimeout = 30 * time.Second // 30s
  21. subscribeMsgTimeout = 30 * time.Second // 30s
  22. sendMsgTimeout = 30 * time.Second // 30s
  23. )
  24. // 创建客户端
  25. func CreateMqttClient(opt MqttClientOpt) (*MqttClient, error) {
  26. // 创建客户端选项
  27. opts := mqtt.NewClientOptions()
  28. opts.AddBroker(opt.Broker)
  29. clientID := "go_mqtt_client"
  30. if len(opt.ClientID) > 0 {
  31. clientID = opt.ClientID
  32. }
  33. opts.SetClientID(clientID)
  34. opts.SetAutoReconnect(true) // 自动重连
  35. opts.SetCleanSession(true) // 不保留会话
  36. opts.SetTLSConfig(opt.TLS)
  37. mqttClient := MqttClient{
  38. subscribeMap: map[string]func(payload []byte){},
  39. }
  40. // 连接成功回调
  41. opts.OnConnect = func(c mqtt.Client) {
  42. zap.L().Info("Connected to MQTT broker", zap.String("clientID", clientID))
  43. // 重新订阅丢失的 Subscribe
  44. mqttClient.ResumeSubscribe()
  45. }
  46. // 连接丢失回调
  47. opts.OnConnectionLost = func(c mqtt.Client, err error) {
  48. fmt.Printf("%s Connection lost: %s\n", clientID, err.Error())
  49. zap.L().Info("Connection lost", zap.String("clientID", clientID), zap.String("err", err.Error()))
  50. }
  51. // 创建客户端
  52. mqttClient.client = mqtt.NewClient(opts)
  53. return &mqttClient, nil
  54. }
  55. func (c *MqttClient) Connect() error {
  56. if c.client.IsConnected() {
  57. return nil
  58. }
  59. // 连接
  60. token := c.client.Connect()
  61. if !token.WaitTimeout(connectTimeout) {
  62. return fmt.Errorf("connect timeout (%s)", c.clientID)
  63. }
  64. return token.Error()
  65. }
  66. // 断开连接
  67. func (c *MqttClient) Disconnect() {
  68. if !c.client.IsConnected() {
  69. return
  70. }
  71. // 清空订阅
  72. c.CleanSubscribe()
  73. // 安全断开,等待最多 2 秒让队列消息发送完
  74. c.client.Disconnect(2000)
  75. }
  76. // 检查是否连接
  77. func (c *MqttClient) IsConnected() bool {
  78. return c.client.IsConnected()
  79. }
  80. // 恢复订阅消息
  81. func (c *MqttClient) ResumeSubscribe() {
  82. for k, v := range c.subscribeMap {
  83. c.subscribeMessage(k, v)
  84. }
  85. }
  86. // 清空所有订阅
  87. func (c *MqttClient) CleanSubscribe() {
  88. for k := range c.subscribeMap {
  89. delete(c.subscribeMap, k)
  90. }
  91. }
  92. // 订阅消息
  93. func (c *MqttClient) subscribeMessage(topic string, callback func(payload []byte)) error {
  94. if !c.client.IsConnected() {
  95. return fmt.Errorf("subscribe failed. (client not connected)")
  96. }
  97. token := c.client.Subscribe(topic, 1, func(c mqtt.Client, m mqtt.Message) {
  98. callback(m.Payload())
  99. })
  100. if !token.WaitTimeout(subscribeMsgTimeout) {
  101. return fmt.Errorf("subscribe timeout (topic:%s)", topic)
  102. }
  103. err := token.Error()
  104. if err != nil {
  105. return err
  106. }
  107. return nil
  108. }
  109. // 订阅消息
  110. func (c *MqttClient) SubscribeMessage(topic string, callback func(payload []byte)) error {
  111. err := c.subscribeMessage(topic, callback)
  112. if err != nil {
  113. return err
  114. }
  115. // 备份subscribe
  116. c.subscribeMap[topic] = callback
  117. return nil
  118. }
  119. // 取消订阅
  120. func (c *MqttClient) UnSubscribeMessage(topic string) error {
  121. if !c.client.IsConnected() {
  122. return fmt.Errorf("unsubscribe failed. (client not connected)")
  123. }
  124. token := c.client.Unsubscribe(topic)
  125. if !token.WaitTimeout(subscribeMsgTimeout) {
  126. return fmt.Errorf("unsubscribe timeout (topic:%s)", topic)
  127. }
  128. err := token.Error()
  129. if err != nil {
  130. return err
  131. }
  132. delete(c.subscribeMap, topic)
  133. return nil
  134. }
  135. // 发送消息,只尝试一次,不管是否送达
  136. func (c *MqttClient) SendMessageOnce(topic string, msg []byte) error {
  137. if !c.client.IsConnected() {
  138. return fmt.Errorf("send message once failed. (client not connected)")
  139. }
  140. token := c.client.Publish(topic, 0, false, msg)
  141. if !token.WaitTimeout(sendMsgTimeout) {
  142. return fmt.Errorf("send message once timeout (topic:%s, msg:%s)", topic, msg)
  143. }
  144. return token.Error()
  145. }
  146. // 发送消息,没收到 ACK 就重发,至少送到一次,可能收到多次
  147. func (c *MqttClient) SendMessage(topic string, msg []byte, retained bool) error {
  148. if !c.client.IsConnected() {
  149. return fmt.Errorf("send message failed. (client not connected)")
  150. }
  151. token := c.client.Publish(topic, 1, retained, msg)
  152. if !token.WaitTimeout(sendMsgTimeout) {
  153. return fmt.Errorf("send message timeout (topic:%s, msg:%s)", topic, msg)
  154. }
  155. return token.Error()
  156. }
  157. // 发送消息,严格一次,不重复,不丢失
  158. func (c *MqttClient) SendMessageMustOnce(topic string, msg []byte, retained bool) error {
  159. if !c.client.IsConnected() {
  160. return fmt.Errorf("send message must once failed. (client not connected)")
  161. }
  162. token := c.client.Publish(topic, 2, retained, msg)
  163. if !token.WaitTimeout(sendMsgTimeout) {
  164. return fmt.Errorf("send message must once timeout (topic:%s, msg:%s)", topic, msg)
  165. }
  166. return token.Error()
  167. }
  168. // 发送消息,没收到 ACK 就重发,至少送到一次,可能收到多次,并保留消息在Broker,新定义的设备会收到消息
  169. func (c *MqttClient) SendMessageRetained(topic string, msg []byte) error {
  170. if !c.client.IsConnected() {
  171. return fmt.Errorf("send message retained failed. (client not connected)")
  172. }
  173. token := c.client.Publish(topic, 1, true, msg)
  174. if !token.WaitTimeout(sendMsgTimeout) {
  175. return fmt.Errorf("send message retained timeout (topic:%s, msg:%s)", topic, msg)
  176. }
  177. return token.Error()
  178. }
  179. // 清除保留在Broker的消息
  180. func (c *MqttClient) CleanMessageRetained(topic string) error {
  181. if !c.client.IsConnected() {
  182. return fmt.Errorf("clean message retained failed. (client not connected)")
  183. }
  184. token := c.client.Publish(topic, 1, true, nil)
  185. if !token.WaitTimeout(sendMsgTimeout) {
  186. return fmt.Errorf("clean message retained timeout (topic:%s)", topic)
  187. }
  188. return token.Error()
  189. }