Bir MQTT broker sağlık kontrolü, cihazlarınızın bağlı olduğu tek şeyi kanıtlamalıdır: bir istemci birkaç saniye içinde bağlanabiliyor, abone olabiliyor, publish edebiliyor ve kendi mesajını geri alabiliyor. Bundan daha azı — bir ping, açık bir port, çalışan bir süreç — broker sessizce girişleri reddederken veya yönlendirmeyi bırakmışken yeşil kalmaya devam edebilir.
Aşağıda bu gidiş-dönüşü (round trip) gerçekleştiren ve izleme araçlarının anlayacağı çıkış kodları döndüren, kullanıma hazır üç betik (Bash, Python, Node.js) ile cron, Kubernetes probe'ları ve alarm örnekleri bulacaksınız.
Bir sağlık kontrolü neyi doğrulamalı
| Kontrol | Yakalar | Kaçırır |
|---|---|---|
TCP portu açık (nc -z) | Kapalı host, firewall, bağlanmamış listener | Kimlik doğrulama hataları, bozuk yönlendirme, dolu diskler, TLS hataları |
| Süreç çalışıyor | Çökmüş broker | "Ayakta" ama sağlıksızken olan her şey |
| Yalnızca CONNECT | Kimlik bilgileri, TLS, protokol sürümü, bağlantı limitleri | Abonelik ACL'leri, mesaj yönlendirme |
| Connect + subscribe + publish + receive | Yukarıdakilerin hepsi, artı yönlendirme ve teslimat gecikmesi | Diğer listener'lardaki veya diğer topic'lerdeki sorunlar |
Otomatikleştirmeniz gereken son satırdır. Her çalıştırma benzersiz bir client ID (aynı ID'ye sahip iki kontrol birbirini düşürür — MQTT client ID en iyi uygulamaları yazısına bakın) ve benzersiz bir topic kullanmalıdır; böylece önceki bir çalıştırmadan gecikmiş bir mesaj, mevcut çalıştırmayı asla sağlıklı gösteremez.
Tekrarlanan bir probe yerine daha kapsamlı, tek seferlik bir teşhis istiyorsanız MQTT broker durumunu kontrol etme yazısıyla başlayın veya broker'ı online MQTT istemcisinde etkileşimli olarak deneyin.
İzleme araçlarının anladığı çıkış kodları
Bu yazıdaki tüm betikler; Icinga, Zabbix, Checkmk, Sensu ve çoğu cron sarmalayıcısının anladığı Nagios eklenti kuralını kullanır:
| Çıkış kodu | Durum | Betiklerimizdeki anlamı |
|---|---|---|
0 | OK | Mesaj gidiş-dönüşü uyarı eşiği içinde başarılı |
1 | WARNING | Gidiş-dönüş başarılı, ancak eşikten yavaş |
2 | CRITICAL | Bağlantı, abonelik, publish veya teslimat başarısız ya da zaman aşımına uğradı |
3 | UNKNOWN | Kontrolün kendisi çalışamadı (eksik araç, hatalı argümanlar) |
Kubernetes, systemd ve cron yalnızca sıfırı sıfır olmayandan ayırt eder; bu yüzden aynı betikler her yerde çalışır.
Bash: mosquitto_sub + mosquitto_pub
En hızlı kontrol, Mosquitto komut satırı istemcilerini kullanır (apt install mosquitto-clients veya brew install mosquitto). Arka plandaki bir subscriber timeout ile tam olarak bir mesaj bekler, publisher da aynı topic'e rastgele bir token gönderir:
#!/usr/bin/env bash
# Round-trip health check. Exit: 0 OK, 2 CRITICAL, 3 UNKNOWN.
HOST="${MQTT_HOST:-broker.connectmq.com}"
PORT="${MQTT_PORT:-8883}"
TIMEOUT="${MQTT_TIMEOUT:-5}"
TOPIC="testmqtt/health/$(hostname)-$$"
TOKEN="ok-$(date +%s)-$RANDOM"
command -v mosquitto_pub >/dev/null || { echo "UNKNOWN - mosquitto clients not installed"; exit 3; }
OPTS=(-h "$HOST" -p "$PORT")
[ "$PORT" = "8883" ] && OPTS+=(--capath /etc/ssl/certs)
[ -n "$MQTT_USER" ] && OPTS+=(-u "$MQTT_USER" -P "$MQTT_PASS")
OUT=$(mktemp)
mosquitto_sub "${OPTS[@]}" -i "health-sub-$$" -t "$TOPIC" -q 1 -C 1 -W "$TIMEOUT" >"$OUT" 2>&1 &
SUB_PID=$!
sleep 1 # give the subscriber time to receive its SUBACK
if ! ERR=$(mosquitto_pub "${OPTS[@]}" -i "health-pub-$$" -t "$TOPIC" -q 1 -m "$TOKEN" 2>&1); then
kill "$SUB_PID" 2>/dev/null
echo "CRITICAL - publish failed: $ERR"; rm -f "$OUT"; exit 2
fi
wait "$SUB_PID"
RECEIVED=$(cat "$OUT"); rm -f "$OUT"
if [ "$RECEIVED" = "$TOKEN" ]; then
echo "OK - round trip via $HOST:$PORT"; exit 0
fi
echo "CRITICAL - no message within ${TIMEOUT}s (${RECEIVED:-no output})"; exit 2Önemli ayrıntılar:
-C 1,mosquitto_sub'ın tek bir mesajdan sonra çıkmasını sağlar;-Wise sonsuza kadar asılı kalmak yerine timeout sonunda vazgeçmesini sağlar.- Betik yalnızca çıkış durumuna güvenmek yerine payload'u token ile karşılaştırır; böylece topic'e düşen başıboş bir mesaj yanlış bir OK üretemez.
- İstemcilerin hata metni (örneğin
Connection error: Connection Refused: not authorised.) çıktı satırına düşer; bir alarmda görmek istediğiniz şey tam olarak budur. Herhangi bir reason code'u MQTT reason code aracında arayabilirsiniz. /etc/ssl/certs; Debian, Ubuntu ve Alpine'deki CA dizinidir. Diğer sistemlerde CA paketinizle birlikte--cafileverin.
Python: gecikme eşikli gidiş-dönüş
Python sürümü (paho-mqtt 2.x) gidiş-dönüş süresini ölçer ve süre bir eşikten yavaşsa WARNING döndürür. Her adımın bir son tarihi vardır; böylece betik bir izleme ajanını asla bloke etmez:
#!/usr/bin/env python3
"""MQTT round-trip health check. Exit: 0 OK, 1 WARNING, 2 CRITICAL."""
import os, sys, threading, time, uuid
import paho.mqtt.client as mqtt
HOST = os.getenv("MQTT_HOST", "broker.connectmq.com")
PORT = int(os.getenv("MQTT_PORT", "8883"))
TIMEOUT = float(os.getenv("MQTT_TIMEOUT", "5"))
WARN_MS = float(os.getenv("MQTT_WARN_MS", "500"))
topic = f"testmqtt/health/{uuid.uuid4().hex[:12]}"
token = uuid.uuid4().hex
connected, subscribed, received = threading.Event(), threading.Event(), threading.Event()
state = {"connack": None, "suback": None, "t0": 0.0, "rtt": 0.0}
def on_connect(client, userdata, flags, reason_code, properties):
state["connack"] = reason_code
connected.set()
if not reason_code.is_failure:
client.subscribe(topic, qos=1)
def on_subscribe(client, userdata, mid, reason_codes, properties):
state["suback"] = reason_codes[0]
subscribed.set()
if not reason_codes[0].is_failure:
state["t0"] = time.perf_counter()
client.publish(topic, token, qos=1)
def on_message(client, userdata, msg):
if msg.payload.decode() == token:
state["rtt"] = (time.perf_counter() - state["t0"]) * 1000
received.set()
def finish(code, text):
print(text)
sys.exit(code)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"health-{uuid.uuid4().hex[:8]}")
client.on_connect, client.on_subscribe, client.on_message = on_connect, on_subscribe, on_message
if os.getenv("MQTT_USER"):
client.username_pw_set(os.environ["MQTT_USER"], os.getenv("MQTT_PASS"))
if PORT == 8883:
client.tls_set() # system CA bundle
try:
client.connect(HOST, PORT, keepalive=30)
except Exception as exc: # DNS, refused, TLS errors, timeouts
finish(2, f"CRITICAL - cannot connect to {HOST}:{PORT}: {exc}")
client.loop_start()
try:
if not connected.wait(TIMEOUT):
finish(2, "CRITICAL - no CONNACK")
if state["connack"].is_failure:
finish(2, f"CRITICAL - connection refused: {state['connack']}")
if not subscribed.wait(TIMEOUT):
finish(2, "CRITICAL - no SUBACK")
if state["suback"].is_failure:
finish(2, f"CRITICAL - subscribe refused: {state['suback']}")
if not received.wait(TIMEOUT):
finish(2, f"CRITICAL - message not delivered within {TIMEOUT:.0f}s")
rtt = state["rtt"]
level, code = ("WARNING", 1) if rtt > WARN_MS else ("OK", 0)
finish(code, f"{level} - round trip {rtt:.0f} ms | rtt={rtt:.1f}ms;{WARN_MS:.0f}")
finally:
client.disconnect()
client.loop_stop()| sonrasındaki metin Nagios performans verisidir; bu verileri grafiğe döken araçlar gidiş-dönüş süresini ücretsiz olarak çizer. CONNACK, SUBACK ve teslimat hatalarını ayırmak gece 3'te çok işe yarar: "subscribe refused" bir ACL değişikliğine, "no CONNACK" aşırı yüklenmiş veya yarı ölü bir broker'a, "not delivered" ise yönlendirmeye veya bridge'e işaret eder. Abonelikler beklenmedik şekilde reddediliyorsa MQTT ACL'nin sessizce reddetmesi yazısı yaygın nedenleri açıklar.
Node.js: MQTT.js 5
Aynı mantık, MQTT.js 5 ve promise API'siyle. Tek bir global zamanlayıcı, bir callback hiç tetiklenmese bile sürecin sonlanmasını garanti eder:
import mqtt from 'mqtt';
import { randomUUID } from 'node:crypto';
const url = process.env.MQTT_URL ?? 'mqtts://broker.connectmq.com:8883';
const timeoutMs = Number(process.env.MQTT_TIMEOUT_MS ?? 5000);
const warnMs = Number(process.env.MQTT_WARN_MS ?? 500);
const topic = `testmqtt/health/${randomUUID().slice(0, 12)}`;
const token = randomUUID();
function finish(code, text) {
console.log(text);
process.exit(code);
}
setTimeout(() => finish(2, `CRITICAL - no round trip within ${timeoutMs} ms`), timeoutMs);
const client = mqtt.connect(url, {
clientId: `health-${randomUUID().slice(0, 8)}`,
username: process.env.MQTT_USER,
password: process.env.MQTT_PASS,
connectTimeout: timeoutMs,
reconnectPeriod: 0, // a health check must not retry silently
});
client.on('error', (err) => finish(2, `CRITICAL - ${err.message}`));
client.on('connect', async () => {
try {
const granted = await client.subscribeAsync(topic, { qos: 1 });
if (!granted[0] || granted[0].qos > 2) finish(2, 'CRITICAL - subscribe refused');
let t0 = 0;
client.on('message', (_topic, payload) => {
if (payload.toString() !== token) return;
const ms = Math.round(performance.now() - t0);
const [code, level] = ms > warnMs ? [1, 'WARNING'] : [0, 'OK'];
client.end(true, () => finish(code, `${level} - round trip ${ms} ms | rtt=${ms}ms;${warnMs}`));
});
t0 = performance.now();
await client.publishAsync(topic, token, { qos: 1 });
} catch (err) {
finish(2, `CRITICAL - ${err.message}`);
}
});reconnectPeriod: 0 ayarı önemlidir: MQTT.js varsayılan olarak sonsuza kadar yeniden bağlanır; bu uygulamalar için harika, hızlı başarısız olması gereken bir probe için ise berbattır. Hatalı kimlik bilgileri, broker'ın bunları nasıl bildirdiğine bağlı olarak Connection refused: Not authorized veya Connection refused: Bad username or password olarak görünür.
Kontrolü zamanlanmış çalıştırmak
Alarmlı cron
# every minute; alert only on non-zero exit
* * * * * /opt/checks/mqtt-health.sh >/tmp/mqtt-health.log 2>&1 || /opt/checks/notify.sh "$(cat /tmp/mqtt-health.log)"Çoğu izleme ajanı (Nagios, Icinga, Checkmk, external check'lerle Zabbix) bu betiklerden herhangi birini doğrudan eklenti olarak çalıştırabilir. MQTT monitör türüne sahip uptime servisleri de işe yarar, ancak bir betik size topic'ler, QoS ve eşikler üzerinde kontrol verir. Tam bir dashboard için probe'u MQTT uptime izleme ve Prometheus ve Grafana ile izleme yazılarında anlatıldığı gibi broker metrikleriyle birleştirin.
Kubernetes probe'ları
Kendi barındırdığınız bir Mosquitto pod'u için yerel bir gidiş-dönüş yapan exec probe'u, tcpSocket probe'undan çok daha faydalıdır. Resmi eclipse-mosquitto image'ı komut satırı istemcilerini zaten içerir:
containers:
- name: mosquitto
image: eclipse-mosquitto:2
ports:
- containerPort: 1883
readinessProbe:
tcpSocket:
port: 1883
periodSeconds: 5
livenessProbe:
exec:
command:
- sh
- -c
- >
mosquitto_sub -h 127.0.0.1 -i k8s-probe-sub -t testmqtt/health/k8s -C 1 -W 5 &
sleep 1;
mosquitto_pub -h 127.0.0.1 -i k8s-probe-pub -t testmqtt/health/k8s -m ok;
wait $!
initialDelaySeconds: 10
periodSeconds: 30
timeoutSeconds: 8
failureThreshold: 3Listener'ınız kimlik doğrulama gerektiriyorsa, ortam değişkeni olarak bağlanmış bir Secret'tan -u/-P ekleyin ve probe kullanıcısına ACL'lerinizde izin verin. timeoutSeconds değerini betiğin kendi timeout'undan büyük tutun ve failureThreshold değerini, bir garbage collection duraklaması sırasındaki tek bir yavaş kontrol broker'ı yeniden başlatmayacak şekilde ayarlayın. Daha fazla dağıtım ayrıntısı Kubernetes üzerinde Mosquitto yazısında.
Yönetilen bir broker'ı izlemek
Yönetilen bir broker'da süreci siz kontrol etmezsiniz, ancak sözleşmenin istemci tarafı hâlâ sizindir: hostname'iniz, kimlik bilgileriniz, ACL'leriniz ve cihazlarınızdan gelen ağ yolu. Harici bir gidiş-dönüş kontrolü; süresi dolmuş kimlik bilgilerini, kazara yapılmış ACL değişikliklerini ve sağlayıcının kendi izlemesinin göremeyeceği, sizin taraftaki ağ sorunlarını yakalar.
Kontrolün kendisini test etmek
Hiç alarm vermeyen bir sağlık kontrolü, hiç olmamasından kötüdür. Ona güvenmeden önce her hata modunu bir kez zorlayın:
- Normal çalıştırın ve
0çıkış kodunu doğrulayın:./mqtt-health.sh; echo $?. - Yanlış bir parola kullanın ve "not authorised" mesajıyla birlikte
2döndüğünü doğrulayın. - Kapalı bir porta yönlendirin (
MQTT_PORT=1999) ve asılı kalmak yerine timeout içinde başarısız olduğunu doğrulayın. - Python veya Node betiğinde
MQTT_WARN_MS=1ayarlayın ve WARNING yolunun1döndürdüğünü doğrulayın. - Kontrol çalışırken token'ın gelişini görmek için online istemcide
testmqtt/health/#topic'ini izleyin.
Sağlık kontrolü mesajlarını canlı izleyin
İyi bir MQTT sağlık kontrolü küçük, hızlı ve dürüsttür: tek bağlantı, tek abonelik, tek mesaj, katı bir timeout ve net bir çıkış kodu. Betiği deponuzda broker yapılandırmanızın yanında tutun, hem ağınızın içinden hem dışından çalıştırın; broker sorunlarından cihazlarınızdan önce haberdar olursunuz.
Sıkça sorulan sorular
1883 portunun açık olduğunu kontrol etmek iyi bir MQTT sağlık kontrolü müdür?
Hayır. Açık bir TCP portu yalnızca bir şeyin dinlediğini kanıtlar. Bir broker TCP bağlantılarını kabul edip yine de her CONNECT'i reddedebilir, abonelikleri geri çevirebilir veya mesaj yönlendirmeyi bırakabilir. Gerçek bir sağlık kontrolü bağlanır, abone olur, publish eder ve kendi mesajını bekler.
Bir MQTT sağlık kontrolü ne sıklıkla çalışmalı?
Harici izleme için 30 ila 60 saniyede bir, Kubernetes probe'ları için 10 ila 30 saniyede bir yaygındır. Her çalıştırmada benzersiz bir client ID ve topic kullanın, timeout'u aralıktan kısa tutun ki kontroller hiçbir zaman üst üste binmesin.
Bir sağlık kontrolü betiği hangi çıkış kodlarını döndürmeli?
Nagios kuralını izleyin: OK için 0, WARNING için 1 (örneğin yavaş bir gidiş-dönüş), CRITICAL için 2 (bağlantı, abonelik veya teslimat başarısız) ve UNKNOWN için 3 (kontrolün kendisi çalışamadı). Cron, systemd ve Kubernetes yalnızca sıfır ile sıfır olmayanı ayırt eder.


