Broker testleri

Yavaş ve Kararsız Hücresel Ağlarda MQTT Testi

tc netem ile 2G, 3G ve LTE gecikmesini, jitter'ı ve paket kaybını emüle edin; mobil IoT için MQTT keep-alive, payload boyutu, QoS ve oturumları ayarlayın.

Güncellendi · 9 dk okuma

Yavaş ve Kararsız Hücresel Ağlarda MQTT Testi

Ofis Wi-Fi'ında kaya gibi sağlam çalışan bir MQTT client'ı, hücresel bir modemde dağılabilir: timeout'a düşen bağlantılar, birkaç dakikada bir keep-alive kopmaları, toplu hâlde gelen QoS 1 mesajları. Çözüm, hücresel ağ koşullarını masanızda yeniden üretmekle başlar: gecikme, jitter, paket kaybı ve sınırlı bant genişliği. Ardından client bu koşullara dayanana kadar keep-alive, payload boyutu, QoS ve oturum ayarlarını düzenlersiniz.

Bu rehber 2G, 3G ve LTE bağlantılarını tc netem ile nasıl emüle edeceğinizi, her profilde hangi MQTT ayarlarını test etmeniz gerektiğini ve konfigürasyonları karşılaştırabilmeniz için teslimatı ve gecikmeyi ölçen bir script'i gösteriyor.

Hücresel ağı farklı kılan ne

Hücresel bağlantılar sadece "yavaş Wi-Fi" değildir. MQTT'nin uzun ömürlü TCP bağlantısını vuran belirli şekillerde bozulurlar:

  • Yüksek ve değişken RTT. Yanıt bekleyen her MQTT alışverişi (CONNACK, SUBACK, PUBACK, PINGRESP) RTT'nin tamamını öder; jitter da bunu öngörülemez kılar.
  • Toplu kayıplar. Handover'lar ve zayıf sinyal arka arkaya birkaç paketi düşürür. TCP bunları yeniden gönderir; bu yüzden MQTT kayıp mesaj yerine saniyeler süren takılmalar görür.
  • Radyonun uyanma gecikmesi. Sessiz bir dönemden sonra modem boşta radyo durumuna geçer; sonraki ilk paket radyonun yeniden bağlanmasını bekler ve o mesaja belirgin bir gecikme eklenir.
  • Operatör seviyesinde NAT (CGNAT). Mobil operatörler cihazları boşta kalan akışları unutan NAT gateway'lerinin arkasına koyar. Bağlantıdan NAT'ın boşta kalma timeout'undan uzun süre hiçbir şey geçmezse bağlantı half-open olur: cihaz bağlı olduğunu sanır, broker ise ondan bir daha haber alamaz. Bkz. MQTT ve NAT timeout'ları.
  • Kotalı veri. Binlerce cihazlık bir filoda header'ların, keep-alive'ın ve TLS handshake'inin her byte'ı para ve pil demektir.

tc netem ile hücresel bağlantıları emüle etmek

Linux'ta netem kuyruk disipliniyle kullanılan tc, bir arayüze gecikme, jitter, kayıp ve hız sınırı ekler. Aşağıdaki profiller tipik koşullar için kaba başlangıç noktalarıdır, belirli bir ağın ölçümleri değildir; elinizde kendi cihazlarınızdan gelen değerler olduğunda onlarla değiştirin.

ProfilGecikme (tek yön)JitterKayıpHız
İyi LTE40 ms10 ms%0,110 mbit
Zayıf LTE120 ms40 ms%11 mbit
3G200 ms50 ms%1384 kbit
2G / EDGE400 ms150 ms%364 kbit
Ölü bölge1500 ms500 ms%1516 kbit

En temiz kurulum, MQTT client'ını bir container içinde çalıştırıp yalnızca o container'ın ağını şekillendirmek ve host'a dokunmamaktır:

A shaped test container
docker run --rm -it --cap-add NET_ADMIN --name cell python:3.12-slim bash

# inside the container
apt-get update && apt-get install -y iproute2 mosquitto-clients
pip install paho-mqtt
Apply and change profiles (inside the container)
# 3G-like: 200 ms ± 50 ms, 1 % loss with 25 % correlation (bursty), 384 kbit/s
tc qdisc add dev eth0 root netem delay 200ms 50ms loss 1% 25% rate 384kbit

tc qdisc change dev eth0 root netem delay 400ms 150ms loss 3% 25% rate 64kbit   # 2G
tc qdisc show dev eth0                                                         # verify
tc qdisc del dev eth0 root                                                     # back to normal

netem yalnızca giden trafiği şekillendirir. Client tarafında bu çoğu test için yeterlidir: gecikme her gidiş-dönüşte bir kez uygulanır ve giden segmentlerin kaybı aynı TCP yeniden gönderim davranışını tetikler. Downlink'i ayrıca şekillendirmeniz gerekiyorsa netem'i bir bridge'in broker tarafına koyun ya da her yönü bağımsız olarak geciktirip kısabilen Toxiproxy kullanın.

MQTT'yi test etmeden önce profili doğrulayın: ping -c 10 broker.connectmq.com eklenen gecikmeyi göstermeli, bir mosquitto_pub da hâlâ geçebilmelidir.

Her profilde teslimatı ölçmek

Bu script (paho-mqtt 2.x) bir subscriber ve bir publisher bağlar, bir grup QoS 1 mesajı gönderir ve kaç tanesinin ulaştığını, bağlanma süresini ve uçtan uca gecikme yüzdeliklerini raporlar. Şekillendirilmiş container içinde her profil için bir kez çalıştırın.

cell_test.py
import json, sys, threading, time, uuid
import paho.mqtt.client as mqtt

HOST, PORT = "broker.connectmq.com", 1883
TOPIC = f"testmqtt/cellular/{uuid.uuid4().hex[:8]}"
N, QOS, SIZE = int(sys.argv[1]), int(sys.argv[2]), int(sys.argv[3])
latencies, done = [], threading.Event()

def on_message(client, userdata, msg):
    latencies.append((time.time() - json.loads(msg.payload)["t"]) * 1000)
    if len(latencies) == N:
        done.set()

def connect(name, keepalive=60):
    ready = threading.Event()
    c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"{name}-{uuid.uuid4().hex[:6]}")
    c.on_connect = lambda *args: ready.set()
    t0 = time.time()
    c.connect(HOST, PORT, keepalive=keepalive)
    c.loop_start()
    if not ready.wait(30):
        sys.exit(f"{name}: no CONNACK within 30 s")
    print(f"{name}: connected in {(time.time() - t0) * 1000:.0f} ms")
    return c

sub = connect("sub")
sub.on_message = on_message
sub.subscribe(TOPIC, qos=QOS)
pub = connect("pub")
time.sleep(2)

pad = "x" * SIZE
for i in range(N):
    pub.publish(TOPIC, json.dumps({"i": i, "t": time.time(), "pad": pad}), qos=QOS)
    time.sleep(0.2)

done.wait(60)
latencies.sort()
pct = lambda q: latencies[min(len(latencies) - 1, int(len(latencies) * q))]
print(f"received {len(latencies)}/{N} at QoS {QOS}, {SIZE}-byte padding")
if latencies:
    print(f"p50 {pct(0.5):.0f} ms  p95 {pct(0.95):.0f} ms  max {latencies[-1]:.0f} ms")
Run it
python cell_test.py 100 1 200    # 100 messages, QoS 1, ~200-byte payloads
python cell_test.py 100 0 200    # same at QoS 0 for comparison
python cell_test.py 20 1 20000   # few large payloads

Publisher ve subscriber container'ın saatini paylaşır; bu yüzden gecikme değerlerinde saat kayması hatası yoktur. İki bağlantı da şekillendirilmiş arayüzden geçer; bu karamsar bir yaklaşımdır (production'da subscriber genellikle bir veri merkezindedir) ama konfigürasyonlar arasındaki farkları net biçimde gösterir. Mesajların damla damla gelişini izlemek için online MQTT client ile normal bir bağlantı üzerinden testmqtt/cellular/# topic'ine subscribe olun.

Keep-alive'ı laboratuvara değil operatöre göre ayarlayın

Hücresel ağda keep-alive'ın iki görevi vardır: NAT kaydını canlı tutmak ve ölü bağlantıları tespit etmek. Keep-alive aralığı, operatörün NAT boşta kalma timeout'undan kısa olmalıdır; aksi hâlde bağlantı iki ping arasında sessizce ölür.

Bu timeout'u bulmak için bağlanın, tamamen boşta kalın (keep-alive test penceresinden büyük olsun) ve giderek uzayan aralıklarla publish edin: 1, 2, 5, 10, 20 dakika. Publish'in broker'a hiç ulaşmadığı (ve client'ın bunu ancak sonraki ping'de ya da yazmada fark ettiği) ilk bekleme süresi NAT limitinizdir. Keep-alive'ı bunun rahatça altına ayarlayın.

Tersi hatayı da test edin: 2G ve "ölü bölge" profillerinde keep-alive'ın kopmalara neden olmadığından emin olun. PINGRESP düzenli olarak birkaç saniye sürüyorsa, agresif timeout'lara sahip bir client sağlıklı bağlantıları düşürebilir. MQTT, broker'ın vazgeçmeden önce keep-alive süresinin bir buçuk katı kadar beklemesine izin verir; bu nedenle çok kısa değerler (15–20 saniyenin altı) yavaş bağlantılarda çok az pay bırakır.

Payload boyutu ve protokol ek yükü

64 kbit/s'lik bir hatta 20 KB'lık bir mesaj uplink'i birkaç saniye meşgul eder ve arkasındaki her şey bekler. 2G profilinde script ile küçük ve büyük payload'ları karşılaştırın; p95 gecikmenin boyutla birlikte arttığını göreceksiniz.

  • Payload'ları kompakt tutun. Kısa JSON anahtarları veya CBOR ve Protobuf gibi ikili formatlar işe yarar. Paket boyutu hesaplayıcı, topic uzunluğunun ve property'lerin mesaj başına ne kadar eklediğini gösterir.
  • Akıllıca gruplayın. On ölçümü taşıyan tek bir mesaj, on ayrı mesajdan daha az ek yük getirir; ancak grubun ortasında kopan bir bağlantı daha çok can yakar. İkisini de test edin.
  • TLS'i hesaba katın. Tam bir TLS handshake'i birkaç gidiş-dönüş ve birkaç kilobyte tutar. 400 ms'lik bir hatta bu, her bağlantı için saniyeler demektir; sık yeniden bağlanmak pahalıdır. Her profilde 8883 portunda TLS ile bağlanma süresini ölçün; arkasındaki sayılar için MQTT bant genişliği ek yükü yazısına bakın.
  • Uzun topic'ler tekrar tekrar gönderiliyorsa MQTT 5 topic alias'larını kullanın; önce broker'ınızın bunları desteklediğini doğrulayın.

Kayıplı hatlarda QoS ve oturumlar

AyarHücresel ağda davranışYapılacak test
QoS 0En az byte; bağlantı koptuğunda yolda olan her şey kaybolurGrubun ortasında hattı kesin, kayıpları sayın
QoS 1Mesaj başına bir ek PUBACK; yeniden bağlanınca tekrar gönderilir, kopya olabilirAynı kesinti, kopyaları kontrol edin
QoS 2Mesaj başına dört paket, her biri RTT öder2G'de throughput'u QoS 1 ile karşılaştırın
Kalıcı oturumSubscription'lar ve kuyruktaki mesajlar yeniden bağlanmalarda korunur2 dakika bağlantıyı kesin, yeniden bağlanın, birikimi sayın

Çoğu hücresel telemetri için alıcı tarafta idempotent işleme ile QoS 1 pragmatik tercihtir. Bunu kalıcı bir oturumla birleştirin (clean_start=False ve MQTT 5'te bir session expiry interval); böylece cihaz tekrar subscribe olmak zorunda kalmaz ve onun için kuyruğa alınan mesajlar kısa bir kesintiyi atlatır. Ayrıntılar MQTT kalıcı oturumlar yazısında.

Yeniden bağlanmaları kötü profillerde açıkça test edin: hattı düşürün (tc qdisc change dev eth0 root netem loss 100%), bekleyin, geri getirin ve client'ın ağı bombardımana tutmak yerine backoff ile yeniden bağlandığını, kesinti sırasında publish edilen QoS 1 mesajlarının hat geri geldiğinde ulaştığını doğrulayın.

Hücresel test kontrol listesi

  1. Önce normal bir ağda smoke test yapın, ardından her netem profilini uygulayın.
  2. Her profil için bağlanma süresini (TLS'li ve TLS'siz), teslim edilen mesaj sayısını ve p50/p95 gecikmeyi kaydedin.
  3. Boşta bekle-sonra-publish et testleriyle operatörün NAT boşta kalma timeout'unu bulun; keep-alive'ı bunun altına ayarlayın.
  4. Hattı 30 saniye ve 5 dakika kesin; yeniden bağlanma backoff'unu, oturumun sürdürülmesini ve QoS 1 yeniden teslimatını doğrulayın.
  5. 2G profilinde payload boyutlarını ve gruplamayı karşılaştırın.
  6. Kritik senaryoları production SIM'iyle gerçek donanımda tekrarlayın.

Hücresel ağlar her zaman laboratuvardan daha yavaş ve daha öngörülemez olacaktır. Her değişiklikte onları emüle etmek "ofiste çalışıyor"u "hareket eden bir kamyonda çalışıyor"a dönüştürür; bu koşulardan elde edilen sayılar da keep-alive, QoS ve payload kararlarınız için savunulabilir bir temel sağlar.

Sıkça sorulan sorular

Hücresel ağda MQTT için keep-alive kaç olmalı?

Operatörün NAT boşta kalma timeout'unun altında kalacak kadar kısa, pili ve veriyi boşa harcamayacak kadar uzun. Birçok kurulum 60 ile 300 saniye arasında bir değere varır, ancak NAT timeout'ları operatöre göre değişir. Kendi SIM kartınızla bağlantının sessizce öldüğü boşta kalma süresini ölçün ve keep-alive'ı bunun rahatça altına ayarlayın.

Paket kaybı MQTT mesajlarının kaybolmasına yol açar mı?

Doğrudan hayır. MQTT, kaybolan segmentleri yeniden gönderen TCP üzerinde çalışır; bu yüzden paket kaybı gecikme sıçramaları ve takılmalar olarak görünür. Mesajlar bağlantı koptuğunda kaybolur: yolda olan QoS 0 mesajları gider, QoS 1 ve 2 mesajları ise oturum korunuyorsa yeniden bağlandıktan sonra tekrar gönderilir.

macOS veya Windows'ta hücresel ağ koşullarını emüle edebilir miyim?

macOS'ta Apple'ın Network Link Conditioner aracı gecikme, kayıp ve bant genişliği profillerini tüm sisteme uygular. Her işletim sisteminde client'ı tc netem içeren bir Linux container'ında çalıştırabilir ya da gecikme ve bant sınırı için client ile broker arasına Toxiproxy koyabilirsiniz.