Docker环境下Paho仅完成首个Topic发布后停止运行问题求助
Hey there, let's break down why your code is only publishing to the first Topic in Docker and acting flaky locally. Looking at your code, the biggest culprit is how you're handling the Paho MQTT client's event loop—plus a few other small tweaks that'll fix this right up.
1. You're Missing the MQTT Event Loop
Paho's MQTT client doesn't handle network operations automatically in the background. When you call publish(), it just queues the message—but without starting the event loop, those queued messages never get sent over the network. That's why only the first one might sneak through (if the connection is still warm), but the rest get stuck.
Fix It: Add Loop Start/Stop
Modify your connector() function to start the event loop after connecting, and clean it up when done:
import uuid # Don't forget to add this import def connector(): port = 1883 # Pro tip: you had 1833 here then 1883 in connect—keep this consistent! msg = {'state': "off"} # Use a unique client ID to avoid connection conflicts client1 = paho.Client(f"shutdown_client_{uuid.uuid4().hex[:8]}", clean_session=True) client1.on_publish = on_publish try: client1.connect(brok, port=port, keepalive=60) client1.loop_start() # This kicks off background network processing print("Connected to MQTT broker") for topic in sub: payload = orjson.dumps(msg) ret = client1.publish(topic, payload) # Check if the message was successfully queued if ret.rc != paho.MQTT_ERR_SUCCESS: print(f"Failed to queue message for {topic}: Error code {ret.rc}") else: print(f"Queued message for {topic}") # Give a tiny buffer to let the last message send before stopping the loop sleep(1) client1.loop_stop() client1.disconnect() except Exception as e: print(f"Connection or publishing failed: {str(e)}")
2. Duplicate Client IDs Are Causing Conflicts
Every time your scheduler runs connector(), you create a new client with the same ID (shutdown_client). If the previous connection didn't clean up properly (super easy to happen in Docker), the MQTT broker might reject the new connection or drop messages. Using a unique ID each time fixes this.
3. Docker Network Gotchas
In Docker, localhost doesn't point to your host machine—so make sure brok is set to the correct address (like your host's LAN IP, or a Docker service name if the broker is also in a container). Test connectivity first by running:
docker exec -it your-container-name ping your-broker-address
Also, the sleep(5) after connecting is overkill, but wrapping the connection in a try-except will help you catch network errors specific to Docker.
4. Clean Up Your Scheduler Loop
Your current while True: sleep(5) is a bit clunky. Replace it with signal handling to keep the process running gracefully:
import signal def main(): scheduler = BackgroundScheduler() trigger = CronTrigger( year="*", month="*", day="*", hour="*", minute="*", second="00", timezone='Europe/Amsterdam' ) scheduler.add_job( connector, trigger=trigger, name="daily shutdown", ) scheduler.start() print("Scheduler running—press Ctrl+C to stop") # Handle Ctrl+C to shut down the scheduler nicely def handle_shutdown(sig, frame): print("\nShutting down scheduler...") scheduler.shutdown() exit(0) signal.signal(signal.SIGINT, handle_shutdown) signal.pause() # Keeps the process alive without a busy loop
Bonus: Add More Logging
In Docker, you can check logs with docker logs your-container-name—adding detailed print statements (like which Topic you're publishing to, and publish return codes) will help you debug exactly where things are failing if issues pop up again.
内容的提问来源于stack exchange,提问作者ajay1738

