如何有效地设置MQTT的连接超时时间?

huangapple go评论177阅读模式
英文:

How to effectively set a connection timeout for MQTT?

问题

我会确保如果程序无法连接到MQTT服务器时崩溃。为此,我将ConnectTimeout设置为10秒,但是当连接到一个不存在的服务器时(名称不存在),调用MQTT会挂起。

package main

import (
	"fmt"
	"time"

	mqtt "github.com/eclipse/paho.mqtt.golang"
)

func main() {
	timeout, _ := time.ParseDuration("10s")
	opts := mqtt.NewClientOptions()
	opts.AddBroker("tcp://this.does.not.resolve.example.coooom:1883")
	opts.SetClientID("monitor")
	opts.SetOrderMatters(false)
	opts.SetConnectRetry(true)
	opts.SetConnectTimeout(timeout)
	client := mqtt.NewClient(opts)
	if token := client.Connect(); token.Wait() && token.Error() != nil {
		panic(fmt.Sprintf("cannot connect to MQTT: %v", token.Error()))
	}
}

如何正确设置超时时间?

英文:

I would like to make sure that my program crashes if it cannot connect to an MQTT server. To this, I set ConnectTimeout to 10 seconds but the call to MQTT hangs when connecting to a server that does not exist (the name does not exist)

package main

import (
	"fmt"
	"time"

	mqtt "github.com/eclipse/paho.mqtt.golang"
)

func main() {
	timeout, _ := time.ParseDuration("10s");
	opts := mqtt.NewClientOptions()
	opts.AddBroker("tcp://this.does.not.resolve.example.coooom:1883")
	opts.SetClientID("monitor")
	opts.SetOrderMatters(false)
	opts.SetConnectRetry(true)
    opts.SetConnectTimeout(timeout)
	client := mqtt.NewClient(opts)
	if token := client.Connect(); token.Wait() && token.Error() != nil {
		panic(fmt.Sprintf("cannot connect to MQTT: %v", token.Error()))
	}
}

How to correctly set the timeout?

答案1

得分: 1

opts.SetConnectRetry(true) 似乎会将连接放入循环中(没有任何错误消息)。通过将代码更改为...

package main

import (
	"fmt"
	"time"

	mqtt "github.com/eclipse/paho.mqtt.golang"
)

func main() {
	timeout, _ := time.ParseDuration("10s")
	opts := mqtt.NewClientOptions()
	opts.AddBroker("tcp://this.does.not.resolve.example.coooom:1883")
	opts.SetClientID("monitor")
	opts.SetOrderMatters(false)
	opts.SetConnectTimeout(timeout)
	client := mqtt.NewClient(opts)
	if token := client.Connect(); token.Wait() && token.Error() != nil {
		panic(fmt.Sprintf("cannot connect to MQTT: %v", token.Error()))
	}
	opts.SetConnectRetry(true)
}

...连接将正确失败(希望客户端在连接断开时仍然能够重新连接)

编辑:我认为将 opts.SetConnectRetry(true) 移动到连接尝试之后不会使其自动重新连接(因为选项已经被使用)。这是一个进退两难的情况。

英文:

opts.SetConnectRetry(true) seems to put the connection in a loop (without any error messages). By changing the code to ...

package main

import (
	"fmt"
	"time"

	mqtt "github.com/eclipse/paho.mqtt.golang"
)

func main() {
	timeout, _ := time.ParseDuration("10s");
	opts := mqtt.NewClientOptions()
	opts.AddBroker("tcp://this.does.not.resolve.example.coooom:1883")
	opts.SetClientID("monitor")
	opts.SetOrderMatters(false)
	opts.SetConnectTimeout(timeout)
	client := mqtt.NewClient(opts)
	if token := client.Connect(); token.Wait() && token.Error() != nil {
		panic(fmt.Sprintf("cannot connect to MQTT: %v", token.Error()))
	}
	opts.SetConnectRetry(true)
}

... the connections fails correctly (and, hopefully, the client will still reconnect if the connection is dropped)

EDIT: I do not think that moving opts.SetConnectRetry(true) after the connection attempt will make it auto-reconnect (because the options have already been used). This is a catch-22 situation.

答案2

得分: 1

SetConnectRetry文档中可以得知:

>SetConnectRetry设置连接函数在失败时是否自动重试连接(当为true时,Connect函数返回的令牌将在连接建立或取消之前不会完成)。如果ConnectRetry为true,则应在OnConnect处理程序中请求订阅。将其设置为TRUE允许在建立连接之前发布消息。

因此,当SetConnectRetry(true)时,Connect()将重试直到成功或停止。不会尝试确定错误是否是永久性的,因为这非常困难(例如,无法解析this.does.not.resolve.example.coooom可能是由于失去互联网连接)。

注意:还有一个单独的选项SetAutoReconnect,用于控制连接断开(在初始连接成功建立后)是否会导致客户端自动重新建立连接。

值得注意的是,添加ConnectRetry选项的主要原因是允许用户在连接建立之前发布消息(它们将在连接建立时自动发送)。这样做的想法是,您应该能够调用Connect,然后在不担心网络状态的情况下使用库(如果网络断开,您显然不会接收到消息!)。

SetConnectTimeout文档在与SetConnectRetry的交互方面可能不太清楚:

>SetConnectTimeout限制客户端在尝试打开与MQTT服务器的连接之前等待的时间。持续时间为0表示永不超时。默认为30秒。目前仅在TCP/TLS连接上运行。

因此,这控制我们等待单个连接尝试完成的时间。当SetConnectRetry(true)时,每次尝试连接时都会使用此间隔(每次尝试都会重新开始)。没有RetryConnectionsFor类型的设置。

关于“确保如果无法连接到MQTT服务器,我的程序会崩溃”,我不太确定您的意思,但我使用的方法类似于以下内容(将适当的操作替换为fmt.PrintLn语句)-请注意,我没有编译/测试这个代码:

package main

import (
	"fmt"
	mqtt "github.com/eclipse/paho.mqtt.golang"
	"time"
)

func main() {
	timeout, _ := time.ParseDuration("10s")
	opts := mqtt.NewClientOptions()
	opts.AddBroker("tcp://this.does.not.resolve.example.coooom:1883")
	opts.SetClientID("monitor")
	opts.SetOrderMatters(false)
	opts.SetAutoReconnect(true).SetMaxReconnectInterval(10 * time.Second)
	opts.SetConnectRetry(true)
	opts.SetConnectTimeout(timeout)
	client := mqtt.NewClient(opts)

	token := client.Connect()

	go func(token mqtt.Token) {
		for {
			done := token.WaitTimeout(1 * time.Minute)
			if done {
				if token.Error() != nil {
					fmt.Println("连接永久失败(很可能是由于调用Disconnect)", token.Error())
				} else {
					fmt.Println("连接已建立")
				}
				return // 我们完成了!

			}
			fmt.Println("异步MQTT连接仍在尝试(可能存在问题!)")
			// 如果要取消连接尝试,可以调用`client.Disconnect()`
		}
	}(token)

	// 做一些其他的事情-您可以发布消息,库将在连接建立时发送它们
}

希望对您有所帮助!

英文:

From the docs for SetConnectRetry:

>SetConnectRetry sets whether the connect function will automatically retry the connection in the event of a failure (when true the token returned by the Connect function will not complete until the connection is up or it is cancelled) If ConnectRetry is true then subscriptions should be requested in OnConnect handler Setting this to TRUE permits messages to be published before the connection is established.

So when SetConnectRetry(true) Connect() will retry until it succeeds or you stop it. No attempt is made to ascertain if an error is permanent because that is very difficult (for example the failure to resolve this.does.not.resolve.example.coooom may be due to loss of internet connection).

Note: There is a separate option SetAutoReconnect that controls whether a dropped connection (after the initial connection is successfully established) will lead to the client automatically re-establishing the connection.

Its worth noting that the main reason for adding the ConnectRetry option was to allow users to publish messages before the connection comes up (they will be sent automatically when the connection is established). The idea is that you should be able to call Connect and then use the library without worrying about the network status (obviously you are not going to receive messages if the network is down!).

The SetConnectTimeout docs may not be quite as clear as they should be in terms of how this interacts with SetConnectRetry:

>SetConnectTimeout limits how long the client will wait when trying to open a connection to an MQTT server before timing out. A duration of 0 never times out. Default 30 seconds. Currently only operational on TCP/TLS connections.

So this controls how long we will wait for a single connection attempt to complete. When SetConnectRetry (true) this interval will be used every time a connection is attempted (starts afresh with each attempt). There is no RetryConnectionsFor type setting.

From self-answer:

>... the connections fails correctly (and, hopefully, the client will still reconnect if the connection is dropped)

You cannot change the options once NewClient(o *ClientOptions) has been called (NewClient takes a copy of the options). Its done this way because changing options while operations are in progress would lead to unpredictable outcomes (e.g. if you call opts.SetConnectRetry(true) after calling Connect then the outcome would depend upon whether the Connect call had completed or not which is unpredictable).

I'm not quite sure what you mean by "make sure that my program crashes if it cannot connect to an MQTT server" but the approach I use is something like the following (substitute the appropriate action for the fmt.PrintLn statements) - note that I have not compiled/tested this:

package main

import (
	"fmt"
	mqtt "github.com/eclipse/paho.mqtt.golang"
	"time"
)

func main() {
	timeout, _ := time.ParseDuration("10s")
	opts := mqtt.NewClientOptions()
	opts.AddBroker("tcp://this.does.not.resolve.example.coooom:1883")
	opts.SetClientID("monitor")
	opts.SetOrderMatters(false)
	opts.SetAutoReconnect(true).SetMaxReconnectInterval(10 * time.Second)
	opts.SetConnectRetry(true)
	opts.SetConnectTimeout(timeout)
	client := mqtt.NewClient(opts)

	token := client.Connect()

	go func(token mqtt.Token) {
	for {
		done := token.WaitTimeout(1 * time.Minute)
		if done {
			if token.Error() != nil {
				fmt.Println("Connection permanently failed (most probably due to call to Disconnect)", token.Error())
			} else {
				fmt.Println("Connection established")
			}
			return // We are done!

		}
		fmt.Println("Async MQTT Connection still trying (there could be an issue!)")
		// Can call `client.Disconnect()` to cancel connection if you want to cancel the connection attempts
	}
	}(token)

	// Do some stuff - you can publish messages and the library will send them when the connection comes up
}

huangapple
  • 本文由 发表于 2021年5月28日 21:27:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/67739769.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定