9.18. MQTT, byte po bytu

V tomto bodě má kamera vše, co potřebuje k tomu, aby mohla komunikovat se skutečnou službou na otevřeném internetu: TCP socket, TLS k jeho zabalení, DNS k pojmenování protějšku a asyncio, které umožňuje témuž skriptu vykonávat jinou práci, zatímco je spojení otevřené. MQTT je první drátový protokol, který to všechno spojuje do něčeho, co nasazený produkt skutečně používá.

Tato stránka pokrývá samotný protokol – formát na drátě, role, které jednotliví účastníci hrají, a kompromisy v jeho návrhu – dostatečně poctivě na to, aby přibalený klient mqtt vypadal jako zřejmé obalení toho, co už je známé, místo jako skok do neznáma.

9.18.1. Pub/sub versus request/response

HTTP – protokol, po kterém většina projektů s kamerou sáhne jako první – je request/response. Klient žádá konkrétní server o konkrétní zdroj; server odpovídá. Každá výměna je jeden na jednoho a oba konce znají adresu toho druhého předem.

MQTT je publish/subscribe. Klienti se připojují ke třetí straně uprostřed, které se říká broker. Publisher posílá zprávu do pojmenovaného topicu, aniž by věděl nebo se staral o to, kdo naslouchá. Subscriber sdělí brokeru, které topicy chce, a poté přijímá každou zprávu publikovanou do těchto topiců. Broker zajišťuje rozesílání: jedno publikování na yard-cam/motion dorazí ke každému zařízení přihlášenému k yard-cam/motion, ať už jich je nula, jedno, nebo padesát.

Z této změny modelu plynou tři věci:

  • Oddělení. Publishers nemusí vědět, že subscribers existují. Subscribers mohou přicházet a odcházet, aniž by si toho publisher všiml. Přidání druhého dashboardu je jeden řádek kódu na novém dashboardu; kamera se nemění.

  • Rozesílání. Broker se postará o každou duplikaci, takže kamera odešle jeden paket bez ohledu na to, kolik zařízení jej čte. To je případ použití, pro který bylo MQTT vytvořeno.

  • Asymetrie. Broker je nyní nezbytnou součástí infrastruktury – bez něj protokol nefunguje. Pro domácí projekty je to obvykle bezplatný veřejný broker (test.mosquitto.org, broker.hivemq.com) nebo malý broker, který si provozujete sami.

Jedna kamera publikuje do topicu yard-cam/motion na brokeru, zatímco dva dashboardy v prohlížeči a jeden cloudový archivátor každý přijímají tutéž zprávu.

9.18.2. Topicy

Topicy jsou řetězce oddělené lomítky. Konvencí je nejobecnější vlevo, nejkonkrétnější vpravo:

yard-cam/motion
yard-cam/temperature
workshop-cam/motion
workshop-cam/temperature/sensor-3

Dva zástupné znaky fungují v přihlášeních (nikoli v publikováních):

  • + odpovídá jediné úrovni. +/motion se přihlásí k motion topicu každé kamery; yard-cam/+ se přihlásí ke každému pod-topicu yard-cam.

  • # odpovídá jedné nebo více koncovým úrovním. yard-cam/# se přihlásí k yard-cam/motion, yard-cam/temperature, yard-cam/temperature/sensor-3 a k čemukoli dalšímu pod yard-cam/. Musí se vyskytovat na konci přihlášení.

Řetězce topiců rozlišují velikost písmen. Podle specifikace úvodní $ označuje topicy interní pro broker ($SYS/...), do kterých by publishers neměli zapisovat.

9.18.3. Formát paketu

MQTT běží přes TCP. Každý řídicí paket začíná jednobytovou pevnou hlavičkou, po níž následuje pole Remaining Length s proměnnou délkou, poté proměnná hlavička specifická pro typ paketu a nakonec payload. Tentýž vnější formát pokrývá každý příkaz – CONNECT, PUBLISH, SUBSCRIBE, PUBACK, DISCONNECT a ostatní – a proto lze MQTT klienta napsat v několika stovkách řádků.

Bytové rozložení paketu MQTT PUBLISH zobrazující byte s typem pevné hlavičky a příznaky, pole Remaining Length s proměnnou délkou, název topicu, volitelný identifikátor paketu a byty payloadu.

Pevná hlavička je jeden byte:

  • Bity 7..4 jsou typ řídicího paketu. 0x3 je PUBLISH (takže první byte obvykle začíná 0x3?). 0x1 je CONNECT, 0x2 CONNACK, 0x8 SUBSCRIBE, 0xC PINGREQ, 0xE DISCONNECT atd.

  • Bity 3..0 jsou příznaky specifické pro typ paketu. U PUBLISH příznaky kódují příznak retransmise DUP, úroveň QoS (2 bity) a příznak RETAIN.

Remaining Length je celé číslo s proměnnou délkou 1 až 4 byty, které počítá každý byte za sebou. Nejvyšší bit každého bytu je značka pokračování – 1 znamená „následuje další byte délky“, 0 znamená „toto je poslední“. Délka pod 128 se vejde do jednoho bytu; větší payloady potřebují více. Maximální kódovaná délka je 256 MiB.

U PUBLISH je proměnnou hlavičkou název topicu – 2bytová délka, poté byty UTF-8 – následovaný 2bytovým identifikátorem paketu, který existuje pouze tehdy, když je QoS 1 nebo 2. Zbývající byty jsou payload, který protokol považuje za neprůhledné byty.

Minimální PUBLISH s QoS 0 hodnoty ok do a/b vypadá takto:

30 07 00 03 'a' '/' 'b' 'o' 'k'
  • 30 – PUBLISH, všechny příznaky nulové.

  • 07 – následuje 7 bytů.

  • 00 03 – délka topicu 3.

  • 'a' '/' 'b' – topic.

  • 'o' 'k' – payload.

Devět bytů na drátě a zpráva dorazí ke každému subscriberovi na a/b na brokeru.

9.18.4. Úrovně QoS

Quality-of-Service řídí, jak usilovně broker (a klient) pracují na zajištění doručení. Tři úrovně:

QoS 0 – nejvýše jednou. Odešli a zapomeň. Paket PUBLISH je odeslán a nikdy potvrzen. Pokud TCP doručí, broker přepošle. Pokud spojení během odesílání spadne, zpráva je ztracena. Většině senzorové telemetrie QoS 0 vyhovuje – jediné vynechané měření teploty v proudu, který vysílá každých 30 sekund, nevadí.

QoS 1 – alespoň jednou. Publisher zahrne identifikátor paketu a čeká na PUBACK. Pokud do vypršení časového limitu žádný PUBACK nedorazí, publisher odešle paket znovu s nastaveným příznakem DUP. Broker může nakonec doručit tutéž zprávu subscriberovi na stejné úrovni dvakrát; subscriber musí být ochoten zvládnout duplikáty.

QoS 2 – právě jednou. Čtyřkrokový handshake (PUBREC / PUBREL / PUBCOMP) zajistí, že zpráva dorazí přesně jednou, a to i napříč opětovnými připojeními. Nákladné na počet obrátek a na stav brokeru. Málokterá aplikace s kamerou jej potřebuje.

Přibalený klient mqtt implementuje QoS 0 a QoS 1; QoS 2 vyvolá výjimku, pokud o něj požádáte. Pro kameru hlásící měření ze senzorů je QoS 0 téměř vždy správná odpověď.

9.18.5. Uchovávané zprávy a poslední vůle

Dvě funkce stojí za poznání, protože mění to, co si broker pamatuje o vašem topicu.

RETAIN. Pokud má PUBLISH nastavený příznak RETAIN, broker zprávu uloží a přepošle ji každému budoucímu subscriberovi v okamžiku, kdy se přihlásí. Tak MQTT řeší „jaká je aktuální hodnota?“ – senzor publikuje svou nejnovější naměřenou hodnotu jako uchovávanou a dashboard, který se přihlásí o deset minut později, stále obdrží nejnovější hodnotu, místo aby čekal na další publikování. Opětovné publikování se stejným topicem přepíše uchovávanou hodnotu; publikování prázdného payloadu ji vymaže.

Poslední vůle. Když se klient připojuje, může brokeru předat „poslední vůli“: topic, payload, QoS a příznak retain. Pokud se tento klient odpojí nečistě – TCP RESET, výpadek napájení, výpadek sítě bez paketu DISCONNECT – broker publikuje vůli jménem klienta. Subscribers ji vidí jako oznámení kamery, že přešla do offline stavu. Samotná kamera vůli nikdy neodesílá; činí tak broker, protože v tu chvíli už je kamera pryč.

9.18.6. Keepalive a opětovné připojení

CONNECT nese interval keepalive v sekundách. Pokud klient po tuto dobu mlčel, broker jej považuje za mrtvý. Aby tomu zabránil, klient periodicky odesílá PINGREQ (jeden byte: 0xC0) a dostává zpět PINGRESP (0xD0) – nejmenší, nejlevnější srdeční tep, který protokol může přenášet. Většina aplikací s kamerou nastavuje keepalive na 30 nebo 60 sekund.

Pokud TCP spojení spadne, obě strany si toho všimnou a připojí se znovu od začátku. Přihlášení provedená před výpadkem jsou ztracena, pokud klient při připojení nepoužil trvalou relaci; pro jednoduché aplikace s kamerou je vzor opětovného přihlášení při opětovném připojení kratší a stejně dobrý.

To stačí ke čtení specifikace MQTT nebo k ručnímu napsání klienta nad socket.socket. Přibalený klient v mqtt přesně to dělá, a navíc nabízí rozumné API pro aplikační kód.