mqtt协议是时下比较好用的发布/订阅模式协议。但是在具体使用的过程中,使用PHP的mqtt扩展会遇到一些奇葩问题,这里是踩坑之后的总结。这里假设读者初步了解mqtt协议。
问题
- 使用基础用法,连接之后直接发布,有可能会出现消息没有发出去的情况。
经过抓包之后发现,发送失败是因为php扩展在发送connect请求后,没有等待回应,直接发送publish请求然后断开连接。mqtt服务端发送connect ack包后没有收到tcp的ack包,当作连接失败,publish请求被抛弃。本地环境网络环境好,所以mqtt服务端可以收到connect ack包,发送成功;但是互联网环境就会有比较大的概率发送失败了。
解决方法为在确认收到connect ack包后再断开连接:
<?php
$client = new Mosquitto\Client();
$client->isok=false;
$client->onConnect(function() use ($client) {
$client->isok=true;
});
$client->connect("127.0.0.1", 1883, 5);
$client->publish('/hello', "Hello from PHP at " . date('Y-m-d H:i:s'));
$client->publish('/hello', "Hello from PHP at " . date('Y-m-d H:i:s'));
while (!$client->isok) {
$client->loop();
}
$client->disconnect();
绝大多数情况下这样写就可以了,但是极端情况下还是可能会发送失败。如果要求一定发送成功,需要修改qos为1或者2。
- 使用以上方法,发送qos>0的消息会失败。
原因是qos>0的消息要求服务器回复publish ack包,而上述方法在确认连接之后就断开连接了,publish ack包没有确认收到,服务端就将此消息抛弃了。解决方案是累加消息次数,收到ack包后将累加器减1,累加器到0了才断开连接:
<?php
$client = new Mosquitto\Client();
$client->num=0;
$client->connect("127.0.0.1", 1883, 5);
$client->onPublish(function() use ($client) {
$client->num--;
});
$client->publish('/hello', "Hello from PHP1 at " . date('Y-m-d H:i:s'));
$client->publish('/hello', "Hello from PHP2 at " . date('Y-m-d H:i:s'),1);
$client->publish('/hello', "Hello from PHP3 at " . date('Y-m-d H:i:s'),2);
$client->num+=3;
while ($client->num>0) {
$client->loop();
}
$client->disconnect();
这种方法对qos=0的消息无效,qos=0时没有ack包,onPublish函数会马上执行,可能连接还没有建立就断开了,出现问题1。
总结
大多数情况下,使用qos=0,使用相对简单的写法就可以了。一般mqtt服务端和php服务器都在一个内网中,异常情况基本可以忽略。
如果想写个更可靠的或者说更好的程序,那么最好的解决方案是:连接后进行阻塞,发一个消息后累加器+1,回调中-1,处理函数末尾阻塞确认消息都发完了才结束。完整代码如下:
<?php
$client = new Mosquitto\Client();
$client->num=0;
$client->isok=false;
$client->onConnect(function() use ($client) {
$client->isok=true;
});
$client->onPublish(function() use ($client) {
$client->num--;
});
while ($client->num>0) {
$client->loop();
}
$client->connect("127.0.0.1", 1883, 5);
$client->publish('/hello', "Hello from PHP1 at " . date('Y-m-d H:i:s'));
$client->publish('/hello', "Hello from PHP2 at " . date('Y-m-d H:i:s'),1);
$client->publish('/hello', "Hello from PHP3 at " . date('Y-m-d H:i:s'),2);
$client->num+=3;
while ($client->num>0) {
$client->loop();
}
$client->disconnect();
网友评论