9.18. MQTT, bajt po bajt¶
U ovoj točki kamera ima sve dijelove koji su joj potrebni za komunikaciju sa stvarnom uslugom na otvorenom internetu: TCP utičnicu, TLS koji je omata, DNS za imenovanje suparnika i asyncio koji omogućuje istoj skripti da obavlja druge poslove dok je veza otvorena. MQTT je prvi mrežni protokol koji sve to spaja u nešto što stvarno isporučeni proizvod doista koristi.
Ova stranica pokriva sam protokol – format na žici, uloge koje svaki sudionik igra i kompromise u njegovu dizajnu – dovoljno iskreno da priloženi mqtt klijent izgleda kao očiti omotač oko onoga što je već poznato umjesto kao skok u nepoznato.
9.18.1. Pub/sub naspram zahtjev/odgovor¶
HTTP – protokol za kojim većina kamera-projekata prvo poseže – radi po načelu zahtjev/odgovor. Klijent traži od određenog poslužitelja određeni resurs; poslužitelj odgovara. Svaka razmjena je jedan-na-jedan i oba kraja unaprijed znaju adresu onog drugog.
MQTT radi po načelu objavi/pretplati (publish/subscribe). Klijenti se povezuju s trećom stranom u sredini koja se naziva broker. Izdavač (publisher) šalje poruku imenovanoj temi (topic) ne znajući i ne mareći tko sluša. Pretplatnik (subscriber) govori brokeru koje teme želi i nadalje prima svaku poruku objavljenu na tim temama. Broker je raspršivač: jedna objava na yard-cam/motion stiže do svakog uređaja pretplaćenog na yard-cam/motion, bez obzira na to ima li ih nula, jedan ili pedeset.
Tri stvari proizlaze iz te promjene modela:
Razdvajanje. Izdavači ne moraju znati da pretplatnici postoje. Pretplatnici mogu dolaziti i odlaziti, a da to izdavač i ne primijeti. Dodavanje druge nadzorne ploče jedna je linija koda na novoj nadzornoj ploči; kamera se ne mijenja.
Raspršivanje. Broker obrađuje svaki duplikat, pa kamera šalje jedan paket bez obzira na to koliko ga uređaja čita. To je upravo slučaj uporabe za koji je MQTT izgrađen.
Asimetrija. Broker je sada nužan dio infrastrukture – bez njega protokol ne radi. Za kućne projekte to je obično besplatan javni broker (
test.mosquitto.org,broker.hivemq.com) ili mali broker koji sami pokrećete.
9.18.2. Teme¶
Teme su nizovi odvojeni kosim crtama. Konvencija je najopćenitije lijevo, najspecifičnije desno:
yard-cam/motion
yard-cam/temperature
workshop-cam/motion
workshop-cam/temperature/sensor-3
Dva zamjenska znaka rade u pretplatama (ne u objavama):
+odgovara jednoj razini.+/motionpretplaćuje na temu pokreta svake kamere;yard-cam/+pretplaćuje na svaku yard-cam podtemu.#odgovara jednoj ili više završnih razina.yard-cam/#pretplaćuje nayard-cam/motion,yard-cam/temperature,yard-cam/temperature/sensor-3i sve drugo podyard-cam/. Mora se pojaviti na kraju pretplate.
Nizovi tema razlikuju velika i mala slova. Prema specifikaciji vodeći $ označava brokerove interne teme ($SYS/...) na koje izdavači ne bi smjeli pisati.
9.18.3. Format paketa¶
MQTT radi preko TCP-a. Svaki upravljački paket počinje jednobajtnim fiksnim zaglavljem iza kojeg slijedi polje Remaining Length promjenjive duljine, zatim promjenjivo zaglavlje specifično za vrstu paketa, pa sadržaj (payload). Isti vanjski format pokriva svaku naredbu – CONNECT, PUBLISH, SUBSCRIBE, PUBACK, DISCONNECT i ostale – zbog čega se MQTT klijent može napisati u nekoliko stotina linija.
Fiksno zaglavlje je jedan bajt:
Bitovi 7..4 su vrsta upravljačkog paketa.
0x3je PUBLISH (pa prvi bajt obično počinje s0x3?).0x1je CONNECT,0x2CONNACK,0x8SUBSCRIBE,0xCPINGREQ,0xEDISCONNECT, itd.Bitovi 3..0 su zastavice specifične za vrstu paketa. Za PUBLISH zastavice kodiraju zastavicu ponovnog slanja DUP, razinu QoS (2 bita) i zastavicu RETAIN.
Remaining Length je cijeli broj promjenjive duljine od 1 do 4 bajta koji broji svaki bajt nakon sebe. Najviši bit svakog bajta je oznaka nastavka – 1 znači „slijedi još jedan bajt duljine”, 0 znači „ovo je posljednji”. Duljina ispod 128 stane u jedan bajt; veći sadržaji koriste više. Najveća kodirana duljina je 256 MiB.
Za PUBLISH promjenjivo zaglavlje je naziv teme – 2-bajtna duljina, zatim UTF-8 bajtovi – iza čega slijedi 2-bajtni identifikator paketa koji postoji samo kada je QoS 1 ili 2. Preostali bajtovi su sadržaj, koji protokol tretira kao neproziran niz bajtova.
Minimalni PUBLISH na razini QoS-0 sadržaja ok na temu a/b jest:
30 07 00 03 'a' '/' 'b' 'o' 'k'
30– PUBLISH, sve zastavice nula.07– slijedi 7 bajtova.00 03– duljina teme 3.'a' '/' 'b'– tema.'o' 'k'– sadržaj.
Devet bajtova na žici i poruka stiže do svakog pretplatnika na a/b na brokeru.
9.18.4. QoS razine¶
Quality-of-Service kontrolira koliko se brano broker (i klijent) trude osigurati isporuku. Tri razine:
QoS 0 – najviše jednom. Pošalji i zaboravi. PUBLISH paket se šalje i nikada se ne potvrđuje. Ako ga TCP isporuči, broker ga prosljeđuje. Ako veza padne usred slanja, poruka je izgubljena. Većina telemetrije senzora dobro funkcionira na QoS 0 – jedno propušteno očitanje temperature u toku koji šalje podatke svakih 30 sekundi nije važno.
QoS 1 – barem jednom. Izdavač uključuje identifikator paketa i čeka PUBACK. Ako PUBACK ne stigne prije isteka vremena, izdavač ponovno šalje s postavljenom zastavicom DUP. Broker može na kraju istu poruku isporučiti pretplatniku dvaput na istoj razini; pretplatnik mora biti spreman obraditi duplikate.
QoS 2 – točno jednom. Rukovanje u četiri koraka (PUBREC / PUBREL / PUBCOMP) osigurava da poruka stigne točno jednom, čak i preko ponovnih povezivanja. Skupo po broju kruženja i stanju brokera. Malo kamera-aplikacija ga treba.
Priloženi mqtt klijent implementira QoS 0 i QoS 1; QoS 2 izaziva iznimku ako ga zatražite. Za kameru koja izvještava o očitanjima senzora, QoS 0 je gotovo uvijek pravi odgovor.
9.18.5. Zadržane poruke i posljednja volja¶
Dvije značajke vrijedi poznavati jer mijenjaju ono što broker pamti o vašoj temi.
RETAIN. Ako PUBLISH ima postavljenu zastavicu RETAIN, broker pohranjuje poruku i prosljeđuje je svakom budućem pretplatniku u trenutku kad se pretplati. Tako MQTT rješava pitanje „koja je trenutna vrijednost?” – senzor objavljuje svoje posljednje očitanje kao zadržano, a nadzorna ploča koja se pretplati deset minuta kasnije i dalje prima najnoviju vrijednost umjesto da čeka sljedeću objavu. Ponovna objava s istom temom prepisuje zadržanu vrijednost; objava praznog sadržaja je briše.
Posljednja volja. Kada se klijent poveže, može brokeru dati „posljednju volju i oporuku”: temu, sadržaj, QoS i zastavicu zadržavanja. Ako se taj klijent odspoji neuredno – TCP RESET, gubitak napajanja, prekid mreže bez DISCONNECT paketa – broker objavljuje oporuku u ime klijenta. Pretplatnici je vide kao obavijest kamere da je otišla izvan mreže. Sama kamera nikada ne šalje oporuku; to čini broker, jer je do tada kamera već nestala.
9.18.6. Keepalive i ponovno povezivanje¶
CONNECT nosi keepalive interval u sekundama. Ako je klijent tako dugo bio tih, broker ga smatra mrtvim. Da bi to spriječio, klijent povremeno šalje PINGREQ (jedan bajt: 0xC0) i dobiva natrag PINGRESP (0xD0) – najmanji i najjeftiniji otkucaj srca koji protokol može nositi. Većina kamera-aplikacija postavlja keepalive na 30 ili 60 sekundi.
Ako TCP veza padne, obje strane to primijete i ponovno se povežu od nule. Pretplate napravljene prije pada su izgubljene osim ako je klijent pri povezivanju koristio trajnu sesiju; za jednostavne kamera-aplikacije obrazac ponovne pretplate pri ponovnom povezivanju je kraći i jednako dobar.
Ovo je dovoljno za čitanje MQTT specifikacije ili za ručno pisanje klijenta preko socket.socket. Priloženi klijent u mqtt upravo to čini, uz razuman API za aplikacijski kod.