9.18. MQTT, byte demi byte¶
Pada tahap ini kamera sudah memiliki semua yang diperlukan untuk berkomunikasi dengan layanan nyata di internet terbuka: socket TCP, TLS untuk membungkusnya, DNS untuk menamakan peer, dan asyncio agar skrip yang sama dapat melakukan pekerjaan lain saat koneksi terbuka. MQTT adalah protokol wire pertama yang menggabungkan semua hal tersebut menjadi sesuatu yang benar-benar digunakan oleh produk yang sudah di-deploy.
Halaman ini membahas protokol itu sendiri -- format on-the-wire, peran yang dimainkan setiap peserta, dan trade-off dalam desainnya -- dengan cukup jelas sehingga klien mqtt yang disertakan terlihat seperti pembungkus alami dari hal yang sudah diketahui, bukan sebuah lompatan kepercayaan.
9.18.1. Pub/sub vs request/response¶
HTTP -- protokol yang paling sering digunakan pertama kali dalam proyek kamera -- adalah request/response. Klien meminta sumber daya tertentu dari server tertentu; server menjawab. Setiap pertukaran adalah satu-ke-satu, dan kedua pihak mengetahui alamat satu sama lain sebelumnya.
MQTT adalah publish/subscribe. Klien terhubung ke pihak ketiga di tengah yang disebut broker. Seorang publisher mengirimkan pesan ke topic bernama tanpa mengetahui atau peduli siapa yang mendengarkan. Seorang subscriber memberi tahu broker topic mana yang diinginkannya dan menerima setiap pesan yang diterbitkan ke topic tersebut setelah itu. Broker adalah fan-out: satu publish pada yard-cam/motion menjangkau setiap perangkat yang berlangganan yard-cam/motion, meskipun ada nol, satu, atau lima puluh di antaranya.
Tiga hal yang mengikuti dari perubahan model tersebut:
Decoupling. Publisher tidak perlu mengetahui keberadaan subscriber. Subscriber dapat datang dan pergi tanpa diketahui publisher. Menambahkan dashboard kedua hanya butuh satu baris kode di dashboard baru; kamera tidak perlu diubah.
Fan-out. Broker menangani setiap duplikat, sehingga kamera hanya mengirim satu paket terlepas dari berapa banyak perangkat yang membacanya. Itulah kasus penggunaan yang menjadi alasan MQTT dibuat.
Asimetri. Broker kini merupakan bagian infrastruktur yang wajib ada -- tanpa broker, protokol tidak berfungsi. Untuk proyek rumahan ini biasanya berupa broker publik gratis (
test.mosquitto.org,broker.hivemq.com) atau broker kecil yang Anda jalankan sendiri.
9.18.2. Topic¶
Topic adalah string yang dipisahkan dengan slash. Konvensinya adalah yang paling umum di kiri, paling spesifik di kanan:
yard-cam/motion
yard-cam/temperature
workshop-cam/motion
workshop-cam/temperature/sensor-3
Dua wildcard bekerja dalam subscriptions (tidak dalam publish):
+mencocokkan satu level.+/motionberlangganan ke topic motion setiap kamera;yard-cam/+berlangganan ke setiap sub-topic yard-cam.#mencocokkan satu atau lebih level yang tersisa.yard-cam/#berlangganan keyard-cam/motion,yard-cam/temperature,yard-cam/temperature/sensor-3, dan apa pun yang berada di bawahyard-cam/. Karakter ini harus muncul di akhir subscription.
String topic bersifat case-sensitive. Menurut spesifikasi, $ di awal menandai topic internal broker ($SYS/...) yang tidak boleh ditulis oleh publisher.
9.18.3. Format paket¶
MQTT berjalan di atas TCP. Setiap paket kontrol dimulai dengan fixed header satu byte diikuti oleh field Remaining Length dengan panjang variabel, lalu variable header spesifik jenis paket, lalu payload. Format luar yang sama mencakup setiap perintah -- CONNECT, PUBLISH, SUBSCRIBE, PUBACK, DISCONNECT, dan lainnya -- itulah mengapa klien MQTT dapat ditulis dalam beberapa ratus baris.
Fixed header adalah satu byte:
Bit 7..4 adalah control packet type.
0x3adalah PUBLISH (sehingga byte pertama biasanya dimulai dengan0x3?).0x1adalah CONNECT,0x2CONNACK,0x8SUBSCRIBE,0xCPINGREQ,0xEDISCONNECT, dan lain-lain.Bit 3..0 adalah flag yang spesifik untuk jenis paket. Untuk PUBLISH, flag mengkodekan flag DUP retransmit, level QoS (2 bit), dan flag RETAIN.
Remaining Length adalah integer dengan panjang variabel 1 hingga 4 byte yang menghitung setiap byte setelahnya. Bit teratas setiap byte adalah penanda kelanjutan -- 1 berarti "byte panjang lain menyusul", 0 berarti "ini yang terakhir". Panjang di bawah 128 muat dalam satu byte; payload yang lebih besar menggunakan lebih banyak. Panjang yang dikodekan maksimal adalah 256 MiB.
Untuk PUBLISH, variable header adalah nama topic -- panjang 2 byte, lalu byte UTF-8 -- diikuti oleh packet identifier 2 byte yang hanya ada jika QoS adalah 1 atau 2. Byte yang tersisa adalah payload, yang diperlakukan sebagai byte opak oleh protokol.
PUBLISH QoS-0 minimal dari ok ke a/b adalah:
30 07 00 03 'a' '/' 'b' 'o' 'k'
30-- PUBLISH, semua flag nol.07-- 7 byte berikut.00 03-- panjang topic 3.'a' '/' 'b'-- topic.'o' 'k'-- payload.
Sembilan byte di wire dan pesan tersampai ke setiap subscriber a/b di broker.
9.18.4. Level QoS¶
Quality-of-Service mengontrol seberapa keras broker (dan klien) bekerja untuk memastikan pengiriman. Tiga level:
QoS 0 -- paling banyak sekali. Kirim dan lupakan. Paket PUBLISH dikirim dan tidak pernah dikonfirmasi. Jika TCP berhasil mengirimkan, broker meneruskannya. Jika koneksi terputus di tengah pengiriman, pesan hilang. Sebagian besar telemetri sensor baik-baik saja di QoS 0 -- satu pembacaan suhu yang terlewat dalam aliran yang mengirimkan setiap 30 detik tidak masalah.
QoS 1 -- setidaknya sekali. Publisher menyertakan packet identifier dan menunggu PUBACK. Jika tidak ada PUBACK yang tiba sebelum timeout, publisher mengirim ulang dengan flag DUP yang disetel. Broker mungkin akhirnya mengirimkan pesan yang sama dua kali ke subscriber pada level yang sama; subscriber harus siap menangani duplikat.
QoS 2 -- tepat sekali. Handshake empat langkah (PUBREC / PUBREL / PUBCOMP) memastikan pesan tiba tepat sekali, bahkan setelah reconnect. Mahal dalam hal round-trip dan status broker. Sedikit aplikasi kamera yang membutuhkannya.
Klien mqtt yang disertakan mengimplementasikan QoS 0 dan QoS 1; QoS 2 akan menimbulkan error jika diminta. Untuk kamera yang melaporkan pembacaan sensor, QoS 0 hampir selalu merupakan jawaban yang tepat.
9.18.5. Pesan yang dipertahankan dan last will¶
Dua fitur yang perlu diketahui karena mengubah apa yang diingat broker tentang topic Anda.
RETAIN. Jika sebuah PUBLISH memiliki flag RETAIN yang disetel, broker menyimpan pesan dan meneruskannya ke setiap future subscriber pada saat mereka berlangganan. Itulah cara MQTT menangani "apa nilai saat ini?" -- sensor menerbitkan pembacaan terbaru yang dipertahankan, dan dashboard yang berlangganan sepuluh menit kemudian tetap menerima nilai terbaru alih-alih menunggu publish berikutnya. Menerbitkan ulang dengan topic yang sama menimpa nilai yang dipertahankan; menerbitkan payload kosong menghapusnya.
Last will. Ketika klien terhubung, ia dapat memberi broker sebuah "last will and testament": sebuah topic, payload, QoS, dan flag retain. Jika klien tersebut terputus secara tidak bersih -- TCP RESET, kehilangan daya, jaringan terputus tanpa paket DISCONNECT -- broker menerbitkan will tersebut atas nama klien. Subscriber melihatnya sebagai notifikasi dari kamera bahwa ia telah offline. Kamera itu sendiri tidak pernah mengirimkan will; broker yang melakukannya, karena saat itu kamera sudah mati.
9.18.6. Keepalive dan reconnect¶
CONNECT membawa interval keepalive dalam detik. Jika klien tidak aktif selama waktu tersebut, broker menganggapnya mati. Untuk mencegah hal itu, klien secara berkala mengirimkan PINGREQ (satu byte: 0xC0) dan mendapatkan kembali PINGRESP (0xD0) -- heartbeat terkecil dan termurah yang dapat dibawa protokol. Sebagian besar aplikasi kamera menetapkan keepalive ke 30 atau 60 detik.
Jika koneksi TCP terputus, kedua pihak menyadarinya dan terhubung kembali dari awal. Subscription yang dibuat sebelum pemutusan hilang kecuali klien menggunakan persistent session saat connect; untuk aplikasi kamera sederhana, pola resubscribe-on-reconnect lebih singkat dan sama baiknya.
Ini sudah cukup untuk membaca spesifikasi MQTT atau membuat klien secara manual melalui socket.socket. Klien yang disertakan di mqtt melakukan persis itu, ditambah API yang masuk akal untuk kode aplikasi.