【问题标题】:Subscribing to MQTT messages using a Goroutine使用 Goroutine 订阅 MQTT 消息
【发布时间】:2018-06-28 17:56:04
【问题描述】:

我目前有一个 Go 代码,可以订阅和打印发布到某个主题的传感器数据。这是我的代码:

package main

import (
    "crypto/tls"
    "flag"
    "fmt"
    //"log"
    "os"
    "os/signal"
    "strconv"
    "syscall"
    "time"
    MQTT "github.com/eclipse/paho.mqtt.golang"
)

func onMessageReceived(client MQTT.Client, message MQTT.Message) {
    //fmt.Printf("Received message on topic: %s\nMessage: %s\n", message.Topic(), message.Payload())
    fmt.Printf("%s\n", message.Payload())
}

func main() {
    //MQTT.DEBUG = log.New(os.Stdout, "", 0)
    //MQTT.ERROR = log.New(os.Stdout, "", 0)
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt, syscall.SIGTERM)

    hostname, _ := os.Hostname()

    server := flag.String("server", "tcp://test.mosquitto.org:1883", "The full url of the MQTT server to connect to ex: tcp://127.0.0.1:1883")
    topic := flag.String("topic", "topic/sensorTemperature", "Topic to subscribe to")


    qos := flag.Int("qos", 0, "The QoS to subscribe to messages at")
    clientid := flag.String("clientid", hostname+strconv.Itoa(time.Now().Second()), "A clientid for the connection")
    username := flag.String("username", "", "A username to authenticate to the MQTT server")
    password := flag.String("password", "", "Password to match username")
    flag.Parse()

    connOpts := MQTT.NewClientOptions().AddBroker(*server).SetClientID(*clientid).SetCleanSession(true)
    if *username != "" {
        connOpts.SetUsername(*username)
        if *password != "" {
            connOpts.SetPassword(*password)
        }
    }
    tlsConfig := &tls.Config{InsecureSkipVerify: true, ClientAuth: tls.NoClientCert}
    connOpts.SetTLSConfig(tlsConfig)

    connOpts.OnConnect = func(c MQTT.Client) {
        if token := c.Subscribe(*topic, byte(*qos), onMessageReceived); token.Wait() && token.Error() != nil {
            panic(token.Error())
        }
    }

    client := MQTT.NewClient(connOpts)
    if token := client.Connect(); token.Wait() && token.Error() != nil {
        panic(token.Error())
    } else {
        fmt.Printf("Connected to %s\n", *server)
    }

    <-c
}

我不想订阅这样的消息,而是想将订阅的代码部分放在 Goroutine 中。我希望能够致电go func onMessageReceived。如果在c.Subscribe 中调用此函数,我该怎么做?以及如何添加sync.WaitGroup 参数?谢谢。

【问题讨论】:

  • 不知道你在问什么——你已经知道如何启动 goroutine 和定义函数参数了,这有什么特别的问题?
  • @Adrian 目前onMessageReceivedc.Subscribe 中作为参数调用。我的问题只是我如何将onMessageReceived 称为 Goroutine。抱歉,我是 Go 编程新手。
  • 好的,我现在明白了。请参阅我的答案以获取可能的解决方案。

标签: go mqtt goroutine


【解决方案1】:

由于您将函数作为参数传递给另一个函数,因此您无法控制调用它的方式。然而,你可以完全控制函数内部发生的事情——这意味着你可以在那里启动一个 goroutine:

func onMessageReceived(client MQTT.Client, message MQTT.Message) {
    go func() {
        fmt.Printf("%s\n", message.Payload())
    }()
}

所以,onMessageReceived 本身仍然会被 MQTT 同步调用,但它只是启动一个 goroutine 并立即返回。您还可以定义一个单独的函数并使用 go 而不是匿名函数来调用它:

func onMessageReceived(client MQTT.Client, message MQTT.Message) {
    go messageHandler(client, message)
}

func messageHandler(client MQTT.Client, message MQTT.Message) {
    fmt.Printf("%s\n", message.Payload())
}

这只是您希望如何组织代码的问题。如果它是一个简短的处理程序,我可能会坚持使用匿名函数(足够短,您可以在一个屏幕上看到整个匿名函数);对于更长的函数,我会将其分解或分解为命名函数。

由于你不能传入任何额外的参数,如果你想使用WaitGroup,它必须是全局的:

var wg = new(sync.WaitGroup)

func onMessageReceived(client MQTT.Client, message MQTT.Message) {
    wg.Add(1)
    go func() {
        defer wg.Done()
        fmt.Printf("%s\n", message.Payload())
    }()
}

【讨论】:

  • 谢谢!如果我想添加一个同步机制(sync.WaitGroup()),我究竟该如何传入呢?因为我一开始就不能传入参数...
  • 为 WG 更新。正如您所指出的,您没有太多选择,因为您无法更改参数,但您仍然可以使用全局 WG。
  • 感谢您的帮助。我将wg.Wait() 放在最后一行之前,即&lt;-c,但现在它没有打印任何内容。可能是什么问题?
  • 很难说 - 如果您无法解决问题,请使用更新的代码发布一个新问题。
猜你喜欢
  • 2018-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多