php mtt擴(kuò)展踩坑總結(jié)

mqtt協(xié)議是時(shí)下比較好用的發(fā)布/訂閱模式協(xié)議。但是在具體使用的過程中,使用PHP的mqtt擴(kuò)展會(huì)遇到一些奇葩問題,這里是踩坑之后的總結(jié)。這里假設(shè)讀者初步了解mqtt協(xié)議。

問題

  1. 使用基礎(chǔ)用法,連接之后直接發(fā)布,有可能會(huì)出現(xiàn)消息沒有發(fā)出去的情況。
    經(jīng)過抓包之后發(fā)現(xiàn),發(fā)送失敗是因?yàn)閜hp擴(kuò)展在發(fā)送connect請(qǐng)求后,沒有等待回應(yīng),直接發(fā)送publish請(qǐng)求然后斷開連接。mqtt服務(wù)端發(fā)送connect ack包后沒有收到tcp的ack包,當(dāng)作連接失敗,publish請(qǐng)求被拋棄。本地環(huán)境網(wǎng)絡(luò)環(huán)境好,所以mqtt服務(wù)端可以收到connect ack包,發(fā)送成功;但是互聯(lián)網(wǎng)環(huán)境就會(huì)有比較大的概率發(fā)送失敗了。
    解決方法為在確認(rèn)收到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();

絕大多數(shù)情況下這樣寫就可以了,但是極端情況下還是可能會(huì)發(fā)送失敗。如果要求一定發(fā)送成功,需要修改qos為1或者2。

  1. 使用以上方法,發(fā)送qos>0的消息會(huì)失敗。
    原因是qos>0的消息要求服務(wù)器回復(fù)publish ack包,而上述方法在確認(rèn)連接之后就斷開連接了,publish ack包沒有確認(rèn)收到,服務(wù)端就將此消息拋棄了。解決方案是累加消息次數(shù),收到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();

這種方法對(duì)qos=0的消息無效,qos=0時(shí)沒有ack包,onPublish函數(shù)會(huì)馬上執(zhí)行,可能連接還沒有建立就斷開了,出現(xiàn)問題1。

總結(jié)

大多數(shù)情況下,使用qos=0,使用相對(duì)簡(jiǎn)單的寫法就可以了。一般mqtt服務(wù)端和php服務(wù)器都在一個(gè)內(nèi)網(wǎng)中,異常情況基本可以忽略。
如果想寫個(gè)更可靠的或者說更好的程序,那么最好的解決方案是:連接后進(jìn)行阻塞,發(fā)一個(gè)消息后累加器+1,回調(diào)中-1,處理函數(shù)末尾阻塞確認(rèn)消息都發(fā)完了才結(jié)束。完整代碼如下:

<?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();
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容