luamqtt
luamqtt copied to clipboard
Connect is blocking
Our broker was recently DDoSed, which caused a disconnect on our luamqtt end, and a reconnect attempt where the connecting routine took over a minute after which our service was automatically killed for being frozen by our watchdog. The stacktrace showed us that in fact the ioloop is not used for connecting in mqtt: https://github.com/xHasKx/luamqtt/blob/929209a2b9d037a77d9f9e28fc8c72429b5c7dfd/mqtt/client.lua#L730 (settimeout is only applied later)
Is this an oversight or by design? Would a rearchitecturing be out of the question so that connect would be asynchronous also? (Connect will always block if there is DNS resolving to be done so this may also not be the perfect solution for everyone). Another option is we could establish a connection ourselves and just provide the connected socket to luamqtt.
@Python1320 , good point. I'll think about how to make connector.connect call non-blocking...
@Python1320 , I found a way how to use settimeout() on almost all stages of the connection lifetime (except DNS resolving). But this will be a breaking change and actually it will use socket.select() technique, which may suffer if you plan to handle hundreds or thousands of simultaneous connections. If you plan to handle such highload - you should consider using another mqtt framework with ioloop builtin into its runtime, like gmqtt for Pyhton or mqtt.js for nodejs.
On the other hand, luamqtt library is built to be small and simple, that's why it does still not have ioloop on all connection stages.
Do you still plan to use it in your project?
I have been struggling with a similar problem, I can't seem to be able to keep a client based on this library connected and listening to the broker for messages without blocking the lua socket. It makes it unusable even in my very simple use case of one publishing client, one listening client with one broker. Am I missing something?
@rafale77 , currently only TCP Connection opening process is blocking and all next stages is async thanks to ioloop, allowing other MQTT clients to work in the same lua script.
Did you used mqtt.run_ioloop(client)
?
Yes I did and I observed that it opens a listener and waits for a message. While waiting, it does block my main program's socket activities.
Yes I did and I observed that it opens a listener and waits for a message. While waiting, it does block my main program's socket activities.
That's because mqtt.run_ioloop(client)
is running an infinity loop to call your mqtt messages handlers when new MQTT messages arrived over TCP connection.
Which runtime is running your code?
If you want to run some parallel calculations or network communications in the same script with luamqtt - you have several options:
- Start a separate OS thread or process to run your logic. In this case you have to exchange data between threads/processes somehow, you should handle it yourself.
- Run your logic in the ioloop started by
mqtt.run_ioloop(client)
. There will be some requirements for your code (it should not block for long). See an example in this test: https://github.com/xHasKx/luamqtt/blob/master/tests/spec/ioloop.lua - Run your own ioloop and adopt the luamqtt to be used in it. If you'll get an example - maybe I can help with that.
- Don't use ioloop at all and try to use luamqtt in sync mode like in this example: https://github.com/xHasKx/luamqtt/blob/master/examples/sync.lua
I am slowly trying out https://github.com/flukso/lua-mosquitto but we are still using luamqtt, but with a 2 second timeout on connect and exponential backoff. A broken MQTT connection is better in my case than a fully frozen process :)
Keeping things simple and flexible is indeed difficult.
In my case I need many things to cooperate at the same time and MQTT freezing the other parts is not acceptable. Imagine a light switch: I want to be able to toggle the light on and off by polling the button status, but I also want to maintain a MQTT connection to tell about the switch status changes. Creating a thread for mqtt sounds like an overkill in this case, but indeed, MQTT must not block toggling the light switch on network troubles.
Did you try OpenResty - https://openresty.org/ ? It's Lua engine has async sockets and supported in luamqtt with appropriate connector
Anyway, there is a way to provide a new method in luamqtt to start connecting async like described in https://github.com/xHasKx/luamqtt/issues/23#issuecomment-653740404 I'll try to implement it on the next week
@Python1320 The main "connect" problem is here; https://github.com/xHasKx/luamqtt/blob/master/mqtt/luasocket.lua#L12
It uses socket.connect
shortcut, which is blocking without a timeout. Can be circumvented easily by using code like this;
local sock = socket.tcp()
sock:settimeout(timeout)
local ok, err = sock:connect(host, port) --> calling `connect` on the socket, NOT the library!
The call will still be blocking, but at least it will timeout at some point and not just hang. If you use an IP instead of a name, you can take out the DNS resolution call as well.
@xHasKx As for using a select
based implementation, that is pretty hard. Essentially you would rewrite Copas inside this library. So imo the better option is to keep this simple, and for non-blocking sockets refer people to Copas.