为什么MQTT客户端重新连接代理时要使用唯一的clientId?

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

why the mqtt client reconnect broker with an unique clientId?

问题

当我运行一个用于测试与 MQTT 代理连接的程序时,客户端总是会丢失连接。以下是我的代码:

var messagePubHandler mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
	fmt.Printf("Received message: %s from topic: %s\n", msg.Payload(), msg.Topic())
}

var connectHandler mqtt.OnConnectHandler = func(client mqtt.Client) {
	fmt.Println("Connected")
}

var connectLostHandler mqtt.ConnectionLostHandler = func(client mqtt.Client, err error) {
	fmt.Printf("Connect lost: %v\n", err)
}

var messageSubHandler mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
	fmt.Printf("Sub message: %s from topic: %s\n", msg.Payload(), msg.Topic())
}

func main() {
	opts := mqtt.NewClientOptions()
	opts.AddBroker("tcp://broker.emqx.io:1883")
	opts.SetClientID("go_mqtt_client")
	opts.SetResumeSubs(true)
	opts.SetAutoReconnect(true)
	opts.SetOrderMatters(false)
	opts.SetCleanSession(false)
	// opts.SetTLSConfig()
	opts.SetDefaultPublishHandler(messagePubHandler)
	opts.OnConnect = connectHandler
	opts.OnConnectionLost = connectLostHandler
	client := mqtt.NewClient(opts)
	if token := client.Connect(); token.Wait() && token.Error() != nil {
		panic(token.Error())
	}

	sub(client)

	s := make(chan os.Signal, 1)
	signal.Notify(s, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)
	select {
	case <-s:
		glog.Infoln("quit")
		glog.Flush()
	}
}

func sub(client mqtt.Client) {
	topic := "topic/test"
	token := client.Subscribe(topic, 2, messageSubHandler)
	token.Wait()
	fmt.Printf("Subscribed to topic: %s\n", topic)
}

结果是:

$ go run cmd/client/client.go 
Connected
Subscribed to topic: topic/test
Connect lost: EOF
Connected
Connect lost: EOF
Connected
...

我知道这是因为客户端ID的问题。但是现在我只运行了一个名为 go_mqtt_client 的客户端,我不知道是否是因为配置的问题。我遇到过这个问题两次。第一次我不记得改了什么,它就可以工作了。但是第二次(现在),我无法解决它。
只有当我更改ClientId时,它才能正常工作...

英文:

When I just run a program to test connection with mqtt broker, client will always be lost.
Here is my code


var messagePubHandler mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
	fmt.Printf(&quot;Received message: %s from topic: %s\n&quot;, msg.Payload(), msg.Topic())
}

var connectHandler mqtt.OnConnectHandler = func(client mqtt.Client) {
	fmt.Println(&quot;Connected&quot;)
}

var connectLostHandler mqtt.ConnectionLostHandler = func(client mqtt.Client, err error) {
	fmt.Printf(&quot;Connect lost: %v\n&quot;, err)
}

var messageSubHandler mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
	fmt.Printf(&quot;Sub message: %s from topic: %s\n&quot;, msg.Payload(), msg.Topic())
}

func main() {
	opts := mqtt.NewClientOptions()
	opts.AddBroker(&quot;tcp://broker.emqx.io:1883&quot;)
	opts.SetClientID(&quot;go_mqtt_client&quot;)
	opts.SetResumeSubs(true)
	opts.SetAutoReconnect(true)
	opts.SetOrderMatters(false)
	opts.SetCleanSession(false)
	// opts.SetTLSConfig()
	opts.SetDefaultPublishHandler(messagePubHandler)
	opts.OnConnect = connectHandler
	opts.OnConnectionLost = connectLostHandler
	client := mqtt.NewClient(opts)
	if token := client.Connect(); token.Wait() &amp;&amp; token.Error() != nil {
		panic(token.Error())
	}

	sub(client)

	s := make(chan os.Signal, 1)
	signal.Notify(s, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)
	select {
	case &lt;-s:
		glog.Infoln(`quit`)
		glog.Flush()
	}
}

func sub(client mqtt.Client) {
	topic := &quot;topic/test&quot;
	token := client.Subscribe(topic, 2, messageSubHandler)
	token.Wait()
	fmt.Printf(&quot;Subscribed to topic: %s\n&quot;, topic)
}

the result is

$ go run cmd/client/client.go 
Connected
Subscribed to topic: topic/test
Connect lost: EOF
Connected
Connect lost: EOF
Connected
...

I know it's because of the clientId. But now I just run a client called go_mqtt_client, I don't know if it's because of the configuration. I've had this problem twice. First time I don't remember what was changed and it can work. But the second time (Right now), I can't fix it.
It just can work if I change the ClientId...

答案1

得分: 2

根据评论,MQTT规范要求:

>如果ClientId代表已连接到服务器的客户端,则服务器必须断开现有的客户端连接[MQTT-3.1.4-2]。

因此,如果您正在使用ClientId go_mqtt_client 进行连接,并且另一个连接使用相同的ClientId,则您的连接将被断开(导致您看到的消息)。

您正在使用的代理服务器(broker.emqx.io)是一个免费的公共代理服务器,并且建议“不要在生产环境中使用”。由于其性质,您无法知道谁还在使用它或者他们使用的ClientId(如果您订阅#,您将看到所有发布到代理服务器的消息!)。

EMQX演示代码使用ClientId go_mqtt_client;这意味着很有可能有其他人正在使用相同的ClientId连接到EMQX公共代理服务器;实际上,在演示代码页面的底部有一条评论(来自“EMQ X”帐户)提到了这一点。

考虑到演示代码使用默认选项,如果连接断开,它将自动重新连接。这意味着您会得到以下事件序列:

  1. 您连接,导致用户2的连接(具有相同ClientId)被断开。
  2. 用户2自动重新连接,导致您的连接被断开。
  3. 您自动重新连接,导致用户2的连接被断开。
  4. 以此类推...

虽然无法百分之百确定这是您遇到的问题的原因,但它肯定是最有可能的原因。由于这是一个免费提供的公共代理服务器,这只是您需要接受的事实;使用随机的ClientId将最大程度地减小(与随机生成的23个字符的ID发生冲突的风险微乎其微)发生这种情况的机会。

英文:

As per the comments the MQTT spec requires that:

>If the ClientId represents a Client already connected to the Server then the Server MUST disconnect the existing Client [MQTT-3.1.4-2].

So if you have a connection using the ClientId go_mqtt_client and another connection comes in with the same ClientId your connection will be dropped (leading to the message you are seeing).

The broker you are using (broker.emqx.io) is a free, public broker that comes with the advice to "Never use it in production". Due to its nature you have no way of knowing who else is using it or what ClientId's they are using (and if you subscribe to # you will see all of the messages being published to the broker!).

The EMQX Demo code uses the ClientID go_mqtt_client; this means that there is a good chance that someone else is using the EMQX public broker with the same ID; in fact this is mentioned in a comment (from the "EMQ X" account) at the bottom of the page with the demo code.

Given that the demo code uses the default options it will automatically reconnect if the connection is dropped. This means you get a sequence of events something like:

  1. You connect; causing the connection for user 2 (with same ClientId) to be dropped)
  2. User 2 automatically reconnects causing your connection to be droped.
  3. You automatically reconnect causing User 2's connection to be dropped.
  4. etc...

While there is no way to be 100% certain that this is the cause of the issue you are seeing, it is certainly the most likely cause. As this is a freely available public broker this is just something you need to live with; using a random ClientId will minimise (the risk of collisions with a random 23 character ID are tiny) the chance of this happening.

huangapple
  • 本文由 发表于 2021年6月3日 16:00:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/67817635.html
匿名

发表评论

匿名网友

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

确定