Paho MQTT 2.0 callback API sürümü değişikliği, Python MQTT betiklerinin bir pip install --upgrade sonrasında aniden çalışmayı bırakmasının en yaygın nedenidir. Bu rehber paho-mqtt 2.0'da tam olarak neyin değiştiğini açıklıyor, hata için tek satırlık düzeltmeyi veriyor ve ardından her callback'in önerilen VERSION2 imzalarına taşınmasını adım adım gösteriyor. Sonunda broker.connectmq.com adresine bağlanan, şablon olarak kullanabileceğiniz eksiksiz ve çalışan bir örneğiniz olacak.
Muhtemelen Gördüğünüz Hatalar
Buraya bir arama motorundan geldiyseniz, büyük ihtimalle şunlardan biriyle karşılaşmışsınızdır:
ValueError: Unsupported callback API version: version 2.0 added a callback_api_version, see docs/migrations.rst for details
TypeError: Client.__init__() missing 1 required positional argument: 'callback_api_version'
DeprecationWarning: Callback API version 1 is deprecated, update to latest version
TypeError: on_connect() takes 4 positional arguments but 5 were given
İlk ikisi istemcinin 1.x yöntemiyle oluşturulmasından kaynaklanır. ValueError, mqtt.Client("my-device") örneğinde olduğu gibi bir client ID'yi ilk konumsal argüman olarak verdiğinizde ortaya çıkar, çünkü o konum artık callback API sürümüne aittir. Üçüncüsü, eski API'yi açıkça seçtiğinizi bildiren bir uyarıdır. Dördüncüsü ise VERSION2'ye geçip bir callback'i eski parametre sayısıyla bıraktığınızda görülür.
paho-mqtt 2.0'da Neler Değişti
Eclipse Paho Python istemcisinin 2024 başında yayımlanan 2.0 sürümü, yıllardır ilk büyük sürümdü. Öne çıkan değişiklik, callback'lerin artık sürümlenmesidir. 1.x'te callback'lere geçirilen parametreler MQTT 3.1.1 ile MQTT 5 arasında farklılık gösteriyordu; bazıları düz tamsayılar, bazıları ise reason code nesneleri kullanıyordu. Sürüm 2 bunları birleştirir: her callback, protokol sürümünden bağımsız olarak aynı parametreleri alır ve sonuç kodları her zaman ReasonCode nesneleridir.
Kodu yeni imzalarla sessizce bozmamak için geliştiriciler seçimi açıkça yapmanızı zorunlu kıldı. Client() fonksiyonunun ilk argümanı artık iki değeri olan callback_api_version parametresidir:
| Değer | Davranış | Önerilir mi? |
|---|---|---|
CallbackAPIVersion.VERSION1 |
Eski 1.x callback imzaları, kullanımdan kaldırma uyarısı verir | Yalnızca geçici bir köprü olarak |
CallbackAPIVersion.VERSION2 |
Yeni birleşik imzalar, her yerde ReasonCode nesneleri |
Evet, tüm yeni ve taşınan kodlar için |
2.0'daki diğer dikkate değer değişiklikler arasında Python 2.7 ve çok eski Python 3 sürümleri için desteğin kaldırılması ve ReasonCodes sınıfının ReasonCode olarak yeniden adlandırılması yer alır. Tam liste resmi paho-mqtt geçiş notlarında bulunur.
Düzeltme 1: Tek Satırlık Köprü (VERSION1)
Üretim ortamı çöktüyse ve hemen geri getirmeniz gerekiyorsa, paho'ya eski davranışı korumasını söyleyin:
import paho.mqtt.client as mqtt
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, client_id="my-device")
Mevcut tüm callback'leriniz değişmeden çalışmaya devam eder. Kullanımdan kaldırma uyarısını görürsünüz; bu, VERSION1'in gelecekteki bir büyük sürümde kaldırılacağının hatırlatıcısıdır. Bunu bir çözüm değil, bir yara bandı olarak görün.
Alternatif bir yara bandı, eski kütüphaneyi pip install "paho-mqtt<2.0" ile sabitlemektir. Bu işe yarar, ancak güvenlik ve hata düzeltmelerini engeller ve genellikle zaten 2.x gerektiren diğer paketlerle çakışır. Sabitleme yerine VERSION1 bayrağını tercih edin ve gerçek geçişi planlayın.
Düzeltme 2: Paho MQTT 2.0 Callback API Sürüm 2'ye Geçiş
Gerçek çözüm VERSION2 geçmek ve callback imzalarını güncellemektir. İşte tüm callback'ler yan yana:
| Callback | 1.x / VERSION1 imzası | VERSION2 imzası |
|---|---|---|
on_connect |
(client, userdata, flags, rc) |
(client, userdata, flags, reason_code, properties) |
on_disconnect |
(client, userdata, rc) |
(client, userdata, disconnect_flags, reason_code, properties) |
on_subscribe |
(client, userdata, mid, granted_qos) |
(client, userdata, mid, reason_code_list, properties) |
on_unsubscribe |
(client, userdata, mid) |
(client, userdata, mid, reason_code_list, properties) |
on_publish |
(client, userdata, mid) |
(client, userdata, mid, reason_code, properties) |
on_message |
(client, userdata, message) |
değişmedi |
on_log |
(client, userdata, level, buf) |
değişmedi |
VERSION1'de MQTT 5 ile bunların bazılarında zaten ek parametreler vardı; VERSION2 bu farkı ortadan kaldırır ve değişikliğin bütün amacı da budur.
Geçiş kontrol listesi
Kodunuzu bu sırayla elden geçirin:
- Her
mqtt.Client(...)çağrısını, diğer tüm argümanları anahtar kelimeyle geçerekmqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=..., ...)olarak değiştirin. on_connectfonksiyonunapropertiesekleyin vercadınıreason_codeyapın.rc == 0ifadesininot reason_code.is_failureile değiştirin (tamsayılarla karşılaştırma hâlâ çalışır, ancak özellik daha okunaklıdır).flags["session present"]ifadesiniflags.session_presentile değiştirin.on_disconnectfonksiyonuna yenidisconnect_flagsparametresini vepropertiesekleyin.on_subscribeiçindegranted_qosyerinereason_code_listkullanın ve her girdiyi kontrol edin: başarı kodlarının değeri 0 ile 2 arasındadır (verilen QoS), başarısız olanlardais_failureayarlıdır.on_publishveon_unsubscribefonksiyonlarınareason_codevepropertiesekleyin.- Kod tabanında
ReasonCodesarayın veReasonCodeolarak değiştirin. - Hiçbir VERSION1 istemcisi kalmadığını kanıtlamak için testlerinizi uyarıları hataya çevirerek (
python -W error::DeprecationWarning) çalıştırın.
Eksiksiz Bir VERSION2 Örneği
Bu betik tüm yaygın callback'leri yeni imzalarla kullanır. Herkese açık MQTT test broker'ının diğer kullanıcılarıyla topic paylaşmamak için k7f3q2 değerini kendi rastgele dizginizle değiştirin:
import time
import uuid
import paho.mqtt.client as mqtt
BROKER = "broker.connectmq.com"
PREFIX = "connectmq/demo/k7f3q2"
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print(f"Connect failed: {reason_code}")
return
print(f"Connected (session present: {flags.session_present})")
client.subscribe(f"{PREFIX}/#", qos=1)
def on_subscribe(client, userdata, mid, reason_code_list, properties):
for rc in reason_code_list:
print("Subscribe rejected:" if rc.is_failure else "Granted QoS:", rc)
def on_publish(client, userdata, mid, reason_code, properties):
print(f"Publish {mid} acknowledged: {reason_code}")
def on_message(client, userdata, msg):
print(f"{msg.topic}: {msg.payload.decode()}")
def on_disconnect(client, userdata, disconnect_flags, reason_code, properties):
print(f"Disconnected: {reason_code}")
client = mqtt.Client(
mqtt.CallbackAPIVersion.VERSION2,
client_id=f"migrate-{uuid.uuid4().hex[:8]}",
protocol=mqtt.MQTTv5,
)
client.on_connect = on_connect
client.on_subscribe = on_subscribe
client.on_publish = on_publish
client.on_message = on_message
client.on_disconnect = on_disconnect
client.connect(BROKER, 1883, keepalive=60)
client.loop_start()
time.sleep(1)
for i in range(3):
client.publish(f"{PREFIX}/test", f"hello {i}", qos=1)
time.sleep(1)
client.disconnect()
client.loop_stop()
Çalıştırdığınızda bağlantıyı, verilen QoS'u, üç publish onayını ve ConnectMQ herkese açık broker'ı tarafından geri yansıtılan üç mesajı görmelisiniz. protocol değerini mqtt.MQTTv311 yapın; aynı callback'ler hiçbir düzenleme olmadan çalışmaya devam eder ve VERSION2'nin vaat ettiği tam olarak budur. Kütüphanenin kendisinde yeniyseniz önce paho mqtt python eğitimiyle başlayın.
Geçişten Sonraki Tuzaklar
1.x'e sabitlenmiş üçüncü taraf kütüphaneler. Paho'yu sarmalayan bazı paketler gereksinimlerinde paho-mqtt<2.0 belirtmiştir. pip show ve pip check ile kontrol edin. Bakımı süren sarmalayıcıların çoğunun güncel sürümleri 2.x'i destekler; onları yükseltin veya eski araçları kendi sanal ortamlarında izole edin.
Konumsal argümanlar. Her mqtt.Client("id", False, None, mqtt.MQTTv311) çağrısı artık bir konum kaymıştır. Hepsini anahtar kelime argümanlarına dönüştürün; bu, gelecekteki yükseltmeleri zahmetsiz hale getirir.
Dekoratörlerle kaydedilen callback'ler. @client.connect_callback() ve benzeri dekoratörleri kullanan kodlarda dekore edilen fonksiyonların imzaları da güncellenmelidir.
Yutulan istisnalar. Paho varsayılan olarak callback'ler içinde fırlatılan istisnaları programınızı çökertmek yerine loglar. Bu nedenle yanlış bir imza "hiçbir şey olmuyor" gibi görünebilir. TypeError görünür olsun diye geçiş sırasında client.enable_logger() ile bir logger bağlayın.
Yeniden bağlanma mantığı. Yeniden bağlanıp bağlanmamaya karar vermek için on_disconnect içinde rc tamsayılarını karşılaştırıyorsanız, bunu reason_code etrafında yeniden yazın. paho-mqtt yeniden bağlanma rehberi sağlam desenler gösterir; Python MQTT TLS rehberi ise üretimde kullanmanız gereken şifreli bağlantıyı ele alır.
Bunun Yerine aiomqtt'yi Düşünmeli misiniz?
Uygulamanız asyncio üzerine kuruluysa, callback'leri taşımak tamamen asenkron bir API'ye geçmek için iyi bir an olabilir. aiomqtt, paho'yu sarmalar ve callback'lerin yerine async for message in client.messages kullanır. aiomqtt ve asyncio eğitimimiz aynı publish/subscribe akışını bu şekilde yazılmış olarak gösterir. Python ve diğer dillerdeki istemcilerin daha geniş bir karşılaştırması için MQTT istemci kütüphanelerine genel bakış yazısına bakın.
Taşınan Kodunuzu ConnectMQ Üzerinde Test Edin
ConnectMQ, bir geçişi doğrulamak için ideal olan broker.connectmq.com adresinde ücretsiz bir herkese açık broker ve üretim için MQTT 3.1.1 ve 5.0, 8883 portunda TLS ve otomatik üretilen kimlik bilgileri sunan tam yönetilen bir MQTT hizmeti işletir. Portalında, taşınan istemcilerin hâlâ beklediğiniz şeyleri publish ettiğini doğrulayabileceğiniz canlı bir topic gezgini bulunur. Herkese açık broker'dan ayrılmaya hazır olduğunuzda istemcilerinizi özel bir bulut MQTT broker üzerinde çalıştırın: kredi kartı olmadan ücretsiz Serverless planına kaydolun.
Sık Sorulan Sorular
paho-mqtt'de callback_api_version nedir?
paho-mqtt 2.0'dan beri mqtt.Client() fonksiyonunun ilk argümanıdır. İstemcinin hangi callback imzalarını kullanacağını seçer: eski 1.x tarzı için VERSION1, yeni birleşik tarz için VERSION2.
VERSION1 mi yoksa VERSION2 mi kullanmalıyım?
VERSION2 kullanın. VERSION1 yalnızca eski kodun çalışmaya devam etmesi için vardır ve bir kullanımdan kaldırma uyarısı yazdırır; dolayısıyla eninde sonunda kaldırılacaktır.
Bir client ID geçtiğimde neden "Unsupported callback API version" hatası alıyorum?
Client ID'yi, artık callback API sürümüne ayrılmış olan ilk konumsal argüman olarak geçtiniz. Önce mqtt.CallbackAPIVersion.VERSION2 geçin, ID'yi ise client_id=... olarak verin.
Geçiş sırasında on_message'ı değiştirmem gerekir mi?
Hayır. on_message(client, userdata, message) her iki sürümde de aynı imzayı korur. Değişiklikler connect, disconnect, subscribe, unsubscribe ve publish callback'lerini etkiler.



