MQTT'yi Apache Kafka'ya bağlamak için broker'daki MQTT topic'lerine subscribe olan ve her mesajı bir Kafka record'u olarak üreten bir bileşen çalıştırırsınız. Üç pratik seçeneğiniz var: bir Kafka Connect MQTT source connector'ı, kendi yazdığınız küçük bir köprü ya da MQTT broker'ınızda yerleşik bir Kafka entegrasyonu. Hangisini seçerseniz seçin zor kısımlar aynıdır: topic'leri eşlemek, sıralamanın korunacağı record key'leri seçmek ve bir şey yeniden başladığında mesaj kaybetmemek.
Bu eğitim üç seçeneği de, çalışan bir Python köprüsü ve beş dakikada koşturabileceğiniz bir testle birlikte anlatıyor.
Neden MQTT ve Kafka birlikte
MQTT ve Kafka farklı problemleri çözer. MQTT; binlerce küçük, aralıklı bağlanan cihazı minik paketler ve mesaj bazında QoS ile bir broker'a bağlar. Kafka ise backend servislerinin büyük ölçekte yeniden okuduğu, birleştirdiği ve işlediği kalıcı, partition'lara bölünmüş bir log'dur. Cihazlar Kafka istemcisi çalıştırmamalı; Kafka da bir milyon kararsız TCP session'ını açık tutmak için tasarlanmadı. Köprü, her iki tarafın da en iyi yaptığı işi yapmasını sağlar. Daha ayrıntılı karşılaştırma için MQTT vs Kafka yazısına bakın.
| Konu | MQTT tarafı | Kafka tarafı |
|---|---|---|
| Adresleme | Binlerce ayrıntılı topic (devices/42/telemetry) | Birkaç geniş topic (iot.telemetry) |
| Sıralama | Topic bazında, istemci session'ı bazında | Partition bazında |
| Teslimat | Mesaj bazında QoS 0 / 1 / 2 | Producer acks, idempotence, transaction'lar |
| Saklama | Retained mesaj + session kuyruğu | Günler veya haftalarca log |
Seçenek 1: Kafka Connect MQTT source
Zaten Kafka Connect çalıştırıyorsanız source connector en az kod gerektiren yoldur. Yaygın iki seçenek:
- Confluent MQTT Source Connector (
io.confluent.connect.mqtt.MqttSourceConnector): Confluent lisansıyla dağıtılır; kendi kurulumunuz için koşulları kontrol edin. - Lenses'in Stream Reactor MQTT Source connector'ı: açık kaynaktır, MQTT topic'lerini Kafka topic'lerine eşleyen bir KCQL ifadesiyle yapılandırılır.
Confluent connector yapılandırması şöyle görünür:
{
"name": "mqtt-telemetry-source",
"config": {
"connector.class": "io.confluent.connect.mqtt.MqttSourceConnector",
"tasks.max": "1",
"mqtt.server.uri": "ssl://broker.connectmq.com:8883",
"mqtt.topics": "testmqtt/kafka/+/telemetry",
"mqtt.qos": "1",
"mqtt.username": "username",
"mqtt.password": "password",
"kafka.topic": "iot.telemetry",
"value.converter": "org.apache.kafka.connect.converters.ByteArrayConverter",
"key.converter": "org.apache.kafka.connect.storage.StringConverter",
"confluent.topic.bootstrap.servers": "kafka:9092",
"confluent.topic.replication.factor": "1"
}
}curl -X POST -H "Content-Type: application/json" \
--data @mqtt-source.json http://localhost:8083/connectorsConnect'in payload'ları gerçekten parse etmesini istemiyorsanız value için ByteArrayConverter kullanmaya devam edin; böylece JSON, Protobuf veya CBOR olduğu gibi geçer. Connector'ın record key'i nasıl doldurduğunu dokümantasyonundan kontrol edin; tam MQTT topic'ini kullanıyorsa her cihaz yine sabit bir partition'a gider ve sıralama için istediğiniz de budur.
Ödünleşim: connector'ı diğer connector'ların yanında kurmak ve izlemek kolaydır, ancak özel mantık (doğrulama, zenginleştirme, cihaz ID'sini key'e çıkarma, birden çok Kafka topic'ine yönlendirme) connector'ın ve Single Message Transform'ların desteklediğiyle sınırlıdır.
Seçenek 2: özel bir Python köprüsü
Özel bir köprü yaklaşık 50 satırdır ve size tam kontrol verir. Bu örnek paho-mqtt 2.x ve confluent-kafka kullanıyor ve her MQTT mesajını ancak Kafka yazmayı onayladıktan sonra onaylıyor:
import os
import paho.mqtt.client as mqtt
from confluent_kafka import Producer
MQTT_HOST = "broker.connectmq.com" # kalıcı session'lar için kendi özel broker'ınızı kullanın
MQTT_TOPIC = "testmqtt/kafka/+/telemetry"
KAFKA_TOPIC = "iot.telemetry"
producer = Producer({
"bootstrap.servers": "localhost:9092",
"enable.idempotence": True, # retry'larda kopya veya sıra bozulması yok
"acks": "all",
"linger.ms": 20, # küçük batch'ler, daha iyi throughput
})
def device_id(topic: str) -> str:
# testmqtt/kafka/<device>/telemetry -> <device>
return topic.split("/")[2]
def on_connect(client, userdata, flags, reason_code, properties):
print("MQTT bağlandı:", reason_code)
client.subscribe(MQTT_TOPIC, qos=1)
def on_message(client, userdata, msg):
mid, qos = msg.mid, msg.qos
def delivered(err, record):
if err is None:
client.ack(mid, qos) # PUBACK yalnızca record Kafka'dayken
else:
print("Kafka'ya yazılamadı, mesaj yeniden iletilecek:", err)
producer.produce(
KAFKA_TOPIC,
key=device_id(msg.topic),
value=msg.payload,
headers=[("mqtt_topic", msg.topic.encode())],
on_delivery=delivered,
)
client = mqtt.Client(
mqtt.CallbackAPIVersion.VERSION2,
client_id="kafka-bridge-1",
clean_session=False, # kalıcı session: biz kapalıyken broker kuyruğa alır
manual_ack=True,
)
client.username_pw_set(os.environ["MQTT_USER"], os.environ["MQTT_PASS"])
client.tls_set()
client.on_connect = on_connect
client.on_message = on_message
client.connect(MQTT_HOST, 8883)
client.loop_start()
try:
while True:
producer.poll(0.1) # teslimat callback'lerini çalıştırır
finally:
producer.flush(10)
client.loop_stop()Bu köprüyü güvenli yapan üç ayrıntı var:
- Sabit bir client ID ile
clean_session=False: broker, köprü yeniden başlarken aboneliği korur ve QoS 1 mesajlarını kuyruğa alır. Broker'ların bunları ne kadar sakladığını MQTT kalıcı session'lar yazısında bulabilirsiniz. manual_ack=True: paho PUBACK'i otomatik göndermez. Süreç Kafka onaylamadan ölürse broker mesajı yeniden iletir.- Record key olarak cihaz ID'si: 42 numaralı cihazın tüm mesajları aynı partition'a, sırayla gider.
Sonuç at-least-once teslimattır. Kafka yazımı ile PUBACK arasında bir çökme kopya üretir; bu yüzden sonraki consumer'lar idempotent olmalıdır (örneğin payload içindeki bir mesaj ID'siyle tekilleştirme).
Sıralama ve key'ler
MQTT'den Kafka'ya pipeline'ların çoğu sıralamada hata yapar. Kurallar şunlar:
- MQTT, aynı QoS ile tek bir istemci session'ı içinde mesajları topic bazında sıralar. Farklı topic'ler veya farklı publisher'lar arasında bir garanti vermez.
- Kafka, record'ları partition bazında sıralar. Varsayılan partitioner ile aynı key her zaman aynı partition'a düşer.
- O hâlde sıralı olması gereken birimle eşleşen bir key kullanın: neredeyse her zaman cihaz ID'si; tam topic değil ve asla rastgele bir değer değil. Bir cihaz
telemetryvestatusmesajlarını iki ayrı topic'e publish ediyorsa ve bunların birbirine göre sıralı olması gerekiyorsa, ikisinde de cihaz ID'sini key yapın.
Köprüyü bir shared subscription ($share/kafka-bridge/testmqtt/kafka/+/telemetry) ile yatayda ölçeklerseniz broker mesajları köprü instance'ları arasında dağıtır. Bu durumda aynı cihazın mesajları aynı anda iki instance'ta işleniyor olabilir ve Kafka'ya sırasız ulaşabilir. Seçenekler: bunu kabul edip sonradan payload'daki timestamp veya sıra numarasıyla yeniden sıralamak ya da MQTT tarafını bölmek (her topic aralığı için bir köprü; örneğin +/telemetry bölge önekine göre ayrılmış). Davranışı MQTT mesaj sıralamasını test etme yazısındaki yaklaşımla doğrulayabilirsiniz.
Seçenek 3: broker'a yerleşik Kafka entegrasyonu
Bazı MQTT broker'ları, mesajları doğrudan broker sürecinden üreten bir Kafka entegrasyonu sunar; genellikle enterprise bir eklenti ya da kural motoru aksiyonu olarak. Bu, bir ara adımı ve işletilecek bir servisi ortadan kaldırır. Ödünleşimler: pipeline'ınız o broker'a bağlanır, yapılandırma kod tabanınızda değil broker'da yaşar ve Kafka'dan gelen backpressure doğrudan broker'ı etkiler. Karar vermeden önce seçenekleri broker karşılaştırma sayfasında karşılaştırın.
Köprüyü uçtan uca test edin
Tek node'lu bir Kafka (KRaft modu) ve key'leri yazdıran bir console consumer başlatın:
docker run -d --name kafka -p 9092:9092 apache/kafka:3.8.0
docker exec -it kafka /opt/kafka/bin/kafka-console-consumer.sh \
--bootstrap-server localhost:9092 --topic iot.telemetry \
--from-beginning --property print.key=true --property print.headers=trueKöprüyü ikinci bir terminalde çalıştırın, ardından iki cihazdan birkaç mesaj publish edin:
for i in 1 2 3 4 5; do
mosquitto_pub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
-u "$MQTT_USER" -P "$MQTT_PASS" -q 1 \
-t testmqtt/kafka/dev-a/telemetry -m "{\"seq\":$i,\"temp\":21.$i}"
mosquitto_pub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
-u "$MQTT_USER" -P "$MQTT_PASS" -q 1 \
-t testmqtt/kafka/dev-b/telemetry -m "{\"seq\":$i,\"temp\":19.$i}"
doneConsumer, dev-a ve dev-b key'lerini her key için artan seq değerleriyle göstermelidir. Aynı mesajların Kafka'ya ulaştığını görmek için online MQTT istemcisinden de publish edebilirsiniz. Ardından asıl önemli olan hata testini çalıştırın. Bu test kalıcı bir session'a dayanır; bu yüzden köprüyü kendi özel broker instance'ınıza (örneğin dedicated bir ConnectMQ broker'ına) yönlendirin. Paylaşımlı test endpoint'i çevrimdışı session saklamaz ve hız sınırlıdır; smoke test için yeterlidir ama kuyruk testleri için uygun değildir.
- Köprüyü
Ctrl+Cile durdurun. - Beş mesaj daha publish edin.
- Köprüyü yeniden başlatın: kuyruktaki mesajlar Kafka'da görünmelidir.
- Kafka'yı durdurun (
docker stop kafka), publish edin, Kafka'yı yeniden başlatın: Kafka kapalıyken köprü PUBACK göndermemelidir; mesajlar toparlanmadan sonra gelir.
- adımda mesaj kaybolursa session'ınız kalıcı değildir (yanlış client ID,
clean_session=Trueya da broker session'ın süresini doldurmuştur). 4. adımda kaybolursa Kafka onaylamadan önce ack gönderiyorsunuz demektir.
Köprünün giriş topic'lerini tarayıcıda izleyin
MQTT'den Kafka'ya bir köprü küçüktür, ama güvenilir olup olmadığını ayrıntılar belirler: kalıcı session'lar, Kafka onaylarına bağlı ack'ler ve ihtiyaç duyduğunuz sıralamayla eşleşen record key'ler. Eşlemenizi karşılıyorsa bir connector ile başlayın, mantık gerektiğinde özel köprüye geçin ve yeniden başlatma testini CI'ınızda tutun ki bu garantiler geçerliliğini korusun.
Sıkça sorulan sorular
Köprü şart mı, yoksa cihazlar doğrudan Kafka'ya publish edebilir mi?
Köprü kullanın. Kafka istemcileri kararlı ağlar, her partition lideriyle uzun ömürlü TCP bağlantıları ve oldukça ağır kütüphaneler bekler. MQTT ise küçük cihazlar ve güvenilmez ağlar için tasarlanmıştır. Cihazlar MQTT konuşsun, veriyi sunucu tarafında Kafka'ya taşıyın.
Bir cihazdan gelen mesajları Kafka içinde nasıl sıralı tutarım?
Her record'a cihazı tanımlayan bir key verin; örneğin MQTT topic'inden çıkarılan cihaz ID'si. Kafka sıralamayı partition bazında korur ve aynı key'e sahip record'lar her zaman aynı partition'a düşer. Yeniden denemelerin sırayı bozmaması ve kopya üretmemesi için idempotent producer'ı açın.
Köprü yeniden başlarsa mesaj kaybeder miyim?
Kalıcı bir session üzerinde QoS 1 ile subscribe olur ve MQTT mesajlarını yalnızca Kafka yazmayı onayladıktan sonra onaylarsanız kaybetmezsiniz. Broker, köprü kapalıyken mesajları kuyruğa alır ve onaylanmamış olanları yeniden iletir. Ara sıra kopya mesaj bekleyin ve consumer'ları idempotent yapın.


