Bir MQTT gecikme testi, bir mesajın publisher'dan broker üzerinden subscriber'a ulaşmasının ne kadar sürdüğünü ölçer. Bunu güvenilir şekilde yapmanın yolu gidiş-dönüş süresini (RTT) tek bir saatle ölçmektir: aynı süreç bir mesaj publish eder, broker'dan geri alır ve monotonik bir zamanlayıcının iki okumasını birbirinden çıkarır. İki makinenin zaman damgalarını karşılaştıran her yöntem, ağ kadar saatler arasındaki farkı da ölçer.
Bu rehber size kopyalayıp kullanabileceğiniz bir gecikme betiği verir, ölçümlerin çoğunu bozan saat tuzaklarını açıklar, yüzdeliklerin nasıl raporlanacağını ve broker'ların ya da bölgelerin nasıl adil karşılaştırılacağını gösterir.
"MQTT gecikmesi" aslında ne demek
Gecikme adı verilen birkaç farklı değer vardır. Kod yazmadan önce hangisine ihtiyacınız olduğuna karar verin:
| Ölçüm | Nereden → nereye | Gereken saat |
|---|---|---|
| Loopback RTT | İstemci publish eder → aynı istemci kendi mesajını alır | Tek saat (kolay, güvenilir) |
| Echo RTT | İstemci A → broker → echo istemcisi B → broker → istemci A | A üzerinde tek saat |
| Tek yönlü gecikme | A host'undaki publisher → B host'undaki subscriber | İki senkronize saat |
| Bağlantı süresi | TCP + TLS handshake + CONNECT → CONNACK | Tek saat |
| Ağ referansı | Broker portuna TCP bağlantısı | Tek saat |
Kapasite planlaması ve broker karşılaştırmaları için genellikle loopback RTT yeterlidir. Broker'ın her iki yöndeki yönlendirme işini ve iki ağ geçişini içerir; tek bir istemciden fazlasına ihtiyaç duymaz.
Saat tuzakları
Kötü gecikme değerlerinin çoğu broker'dan değil, saatten gelir:
- Duvar saati sıçrar. Python'da
time.time()veya JavaScript'teDate.now(), NTP'nin her an düzeltebileceği sistem saatini izler. Aralıklar için monotonik bir saat kullanın: Python'datime.perf_counter(), Node.js'teperformance.now()veyaprocess.hrtime.bigint(). - İki makine hiçbir zaman tam olarak uyuşmaz. NTP olsa bile host'lar arasında bir milisaniye veya daha fazla fark olması yaygındır; iyi bir zaman kaynağı olmayan cihazlarda bu fark çok daha büyüktür. Tek yönlü ölçüm bu hatayı doğrudan devralır — negatif gecikmeler bile görebilirsiniz.
- RTT / 2 bir tahmindir. Yükleme ve indirme yolları farklı olabilir (hücresel ağ, ADSL, tıkanmış uplink'ler). RTT'yi RTT olarak raporlayın; ikiye böldüğünüzde bunu açıkça belirtin.
- Küçük cihazlarda zamanlayıcı çözünürlüğü. Bir mikrodenetleyicideki
millis()1 ms çözünürlüğe sahiptir; tek bir okumaya güvenmek yerine çok sayıda ölçümün ortalamasını alın.
Bir loopback RTT betiği
Bu betik (paho-mqtt 2.x) benzersiz bir topic'e abone olur, sıra numaralarını tek tek publish eder ve bir sonrakini göndermeden önce her mesajın geri gelmesini bekler. Bu kapalı döngü tasarımı, kuyruk etkileri olmadan boştaki bir yolun gecikmesini ölçer.
import os, statistics, sys, threading, time, uuid
import paho.mqtt.client as mqtt
HOST = os.getenv("MQTT_HOST", "broker.connectmq.com")
PORT = int(os.getenv("MQTT_PORT", "1883"))
N = int(sys.argv[1]) if len(sys.argv) > 1 else 500
QOS = int(os.getenv("MQTT_QOS", "1"))
WARMUP = 20
TOPIC = f"testmqtt/latency/{uuid.uuid4().hex[:8]}"
ready, got = threading.Event(), threading.Event()
state = {"expected": -1, "t0": 0.0}
samples, lost = [], 0
def on_connect(client, userdata, flags, reason_code, properties):
client.subscribe(TOPIC, qos=QOS)
def on_subscribe(client, userdata, mid, reason_code_list, properties):
ready.set()
def on_message(client, userdata, msg):
if int(msg.payload) == state["expected"]:
rtt = (time.perf_counter() - state["t0"]) * 1000
if state["expected"] >= WARMUP:
samples.append(rtt)
got.set()
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
if os.getenv("MQTT_USER"):
client.username_pw_set(os.getenv("MQTT_USER"), os.getenv("MQTT_PASS"))
client.on_connect, client.on_subscribe, client.on_message = on_connect, on_subscribe, on_message
client.connect(HOST, PORT, keepalive=30)
client.loop_start()
ready.wait(10)
for i in range(N + WARMUP):
got.clear()
state["expected"], state["t0"] = i, time.perf_counter()
client.publish(TOPIC, str(i), qos=QOS)
if not got.wait(5):
lost += 1
time.sleep(0.02) # ölçümler arasında yolu boşta bırak
client.loop_stop()
q = statistics.quantiles(samples, n=100)
print(f"{HOST}:{PORT} QoS {QOS} samples {len(samples)} lost {lost}")
print(f"min {min(samples):.1f} p50 {q[49]:.1f} p95 {q[94]:.1f} "
f"p99 {q[98]:.1f} max {max(samples):.1f} ms")pip install "paho-mqtt>=2"
python mqtt_rtt.py 1000
MQTT_PORT=8883 python mqtt_rtt.py 1000 # TLS için client.tls_set() gerekir
MQTT_QOS=0 python mqtt_rtt.py 1000İlk WARMUP mesaj atılır, çünkü soğuk önbelleklerin ve bağlantı ısınmasının bedelini öderler. TLS ile test etmeniz gerekiyorsa connect() öncesine client.tls_set() ekleyin ve 8883 portunu kullanın — handshake bir kez gerçekleştiği için mesaj başına RTT'yi değil, bağlantı süresini etkiler.
Betiğe geçmeden önce online MQTT istemcisinde hızlı bir kontrol yapmaya değer: testmqtt/latency/# topic'ine abone olun, bir mesaj publish edin ve geri geldiğini doğrulayın. Bu çalışıyorsa betik de çalışacaktır.
Ağı broker'dan ayırın
40 ms'lik bir RTT, bunun ne kadarının ağa ait olduğunu bilene kadar size hiçbir şey söylemez. Referans olarak broker portuna yalın TCP bağlantı süresini ölçün:
python3 -c "import socket,time
for _ in range(5):
t=time.perf_counter(); socket.create_connection(('broker.connectmq.com',1883)).close()
print(f'{(time.perf_counter()-t)*1000:.1f} ms')"Bir TCP bağlantısı bir ağ gidiş-dönüşüne karşılık gelir. QoS 0 ile ölçtüğünüz MQTT loopback RTT bu referansa yakınsa broker neredeyse hiçbir şey eklemiyordur ve kalan gecikme mesafeden kaynaklanır. Çok daha yüksekse broker'a, istemciye veya yola bakın — yüksek gecikme sorun giderme yazısı yaygın nedenleri ele alır.
Işık fiberde milisaniyede yaklaşık 200 km yol alır; yani 2.000 km uzaktaki bir broker, daha hiçbir yazılım çalışmadan en az ~20 ms RTT'ye mal olur. Hiçbir broker ayarı fiziği yenemez; cihazlarınıza yakın bir bölge seçin.
Ortalama değil, yüzdelik raporlayın
Gecikme dağılımlarının uzun kuyrukları vardır. 12 ms'lik bir ortalama 300 ms'lik bir p99'u gizleyebilir ve cihazlarda timeout'a yol açan, dashboard'ları takılttıran şey p99'dur. Her zaman en az şunları raporlayın:
- min — ağın belirlediği taban; çalıştırmalar arasında kararlı olmalıdır.
- p50 — tipik deneyim.
- p95 / p99 — kuyruk; yapılandırmalar arasında bu değerleri karşılaştırın.
- max ve lost — tekil aykırı değerler ve timeout'lar gürültü değil, sinyaldir.
Raporladığınız yüzdelik için yeterli örnek toplayın. 100 örnekle p99 pratikte tek bir mesajdır; bir p99 değeri vermeden önce en az 1.000 örnek hedefleyin.
Gecikmeyi neler değiştirir
| Etken | RTT'ye etkisi |
|---|---|
| QoS 0 → QoS 1 | Teslimat PUBACK'i beklemez, ancak yük altında broker tarafındaki kalıcılık ve inflight limitleri süre ekleyebilir |
| QoS 2 | Her atlamada dört paketlik el sıkışma; belirgin şekilde daha yavaş, bkz. QoS 0 vs 1 vs 2 benchmark |
| TLS | Bağlanırken handshake maliyeti; mesaj başına küçük şifreleme maliyeti |
| WebSockets | Ek çerçeveleme ve çoğu zaman bir proxy atlaması |
| Payload boyutu | Büyük payload'lar yavaş bağlantılarda daha uzun sürer |
| Broker yükü | Kuyruklar büyür, önce kuyruk gecikmesi (tail latency) artar |
| İstemci callback'leri | Yavaş bir on_message sonraki tüm mesajları geciktirir |
Gecikmenin yük altında nasıl davrandığını görmek için, bir yük üreteci trafik gönderirken bu betiği çalıştırın — yöntem MQTT broker yük testi yazısında anlatılıyor. Boştaki RTT en iyi durumunuzdur; yük altındaki RTT ise planlamayı üzerine yaptığınız değerdir.
Broker'ları ve bölgeleri adil karşılaştırmak
Bir karşılaştırma, ancak broker dışındaki her şey aynı olduğunda adildir:
- Aynı makineden ve aynı ağdan ölçün; tercihen cihazlarınızın veya backend'inizin bulunduğu bölgedeki bir VM'den.
- Her hedef için aynı QoS, payload, port ve TLS ayarını kullanın.
- Önce tüm A'yı sonra tüm B'yi ölçmek yerine hedefleri dönüşümlü (A, B, A, B) çalıştırın; böylece geçici bir ağ aksaklığı her ikisini de etkiler.
- Her hedef için min, p50, p99 ve lost değerlerini TCP referansıyla birlikte tek bir tabloya kaydedin.
- Sonuca varmadan önce günün başka bir saatinde tekrarlayın.
Çok bölgeli kararlar için kullanıcılarınızın bulunduğu her bölgede küçük bir VM başlatın ve aynı betiği her broker uç noktasına karşı çalıştırın. (İstemci bölgesi, broker bölgesi) başına p50/p99 değerlerinden oluşan matris, elde edebileceğiniz en dürüst veridir.
Gecikme testi kontrol listesi
- RTT için monotonik bir saat ve tek bir süreç kullanın.
- Isınma mesajlarını atın; bağlantı süresini ayrıca ölçün.
- 1.000'den fazla örnek toplayın ve min, p50, p95, p99, max ile lost değerlerini raporlayın.
- Her MQTT sonucunun yanına bir TCP referansı kaydedin.
- Boşta ve yük altında, cihazlarınızın gerçekten kullandığı ağdan test edin.
- Betiği deponuzda tutun ve broker veya ağ değişikliklerinden sonra yeniden çalıştırın.
Gidiş-dönüşü tarayıcınızda kontrol edin
Güvenilir bir MQTT gecikme testi büyük ölçüde disiplin meselesidir: tek saat, yeterli örnek, ortalama yerine yüzdelikler ve sayının ne kadarının ağa ait olduğunu gösteren bir referans. Bunları doğru yaptığınızda 15 dakikalık bir betik, üretici benchmark'larının asla yanıtlamayacağı soruları yanıtlar.
Sıkça sorulan sorular
Senkronize saatler olmadan MQTT gecikmesini nasıl ölçerim?
Gidiş-dönüş süresini tek bir saatle ölçün: bir istemci kendisinin de abone olduğu bir topic'e (ya da mesajı geri yayınlayan bir echo istemcisine) publish eder ve aynı süreç gönderme ile alma anını monotonik bir saatle damgalar.
MQTT gidiş-dönüş süresi tek yönlü gecikmenin iki katı mıdır?
Yalnızca yaklaşık olarak. RTT / 2 her iki yönün simetrik olduğunu varsayar; hücresel veya asimetrik bağlantılarda bu çoğu zaman doğru değildir. Gerçek tek yönlü değerler için, ölçtüğünüz gecikmeden çok daha hassas senkronize edilmiş saatler gerekir.
İlk MQTT mesajım neden diğerlerinden çok daha yavaş?
İlk mesajlar DNS çözümlemesinin, TCP ve TLS ısınmasının, aboneliğin kurulmasının ve soğuk önbelleklerin bedelini öder. Ölçüm kaydetmeden önce bir ısınma grubunu atın ve bağlantı süresini ayrıca ölçün.


