njh / ruby-mqtt

Pure Ruby gem that implements the MQTT protocol, a lightweight protocol for publish/subscribe messaging.
http://www.rubydoc.info/gems/mqtt
MIT License
541 stars 135 forks source link

Graceful Termination #100

Open rlugge opened 7 years ago

rlugge commented 7 years ago

I've managed to work around most of the issues involved in using blocking code, but I've got a small annoyance left.

Because I need this as a long-running process running alongside a rails process, I need to be able to let it run in the background indefinitely, until a termination command is recieved.

So far, my best effort is to trap the Sigint signall and call client.disconnect(), but that simply throws a new error, since synchronize can't be called from trap context.

How am I supposed to trigger a graceful shutdown?

njh commented 7 years ago

Ah, I have not encountered this. I will look into how best to trap signals and trigger a clean disconnection.

Out of interest, what is the problem you are trying to work round?

rlugge commented 7 years ago

A) Sigint will currently terminate the app in the middle of anything it is doing. This could lead to half-handled messages or similar issues. B) It generates a lot of garbage that gets output into the log.

So, basically the last two, but it's entirely possible that the first one will become an issue.

This may also be partially a workflow confusion. How do you normally terminate listening? With the MQTT code being blocking, it's kind of difficult for other code to run.

rlugge commented 7 years ago

It doesn't solve some of the underlying issues, but I actually realized I have a 'better' solution now:

client = MQTT::Client.new
client.host = '45.56.89.124'
# client.ssl = true
# client.cert_file = path_to('client.pem')
# client.key_file  = path_to('client.key')
# client.ca_file   = path_to('root-ca.pem')

running = true

trap("SIGINT") do
  running = false
end

Thread.new do
  begin
    $stdout.sync = true
    client.connect() do |client|
      topic = '/application/#'
      puts "Subscribing to: " + topic
      client.subscribe(topic)
      client.get do | topic, message |
        MQTTEventHandler.call(topic, message)
      end
    end
  rescue MQTT::ProtocolException => error
    puts "MQTT failure: " + error.message
  end
end

while(running)
  sleep 1
end

client.disconnect

Of course, from here I'm going to spend a lot of time cleaning up -- adding reconnect logic to automatically attempt a reconnect and so on.

teaching-innovation commented 7 years ago

Hi, I have a ruby script that is working but continues to log a warning in my terminal even after the script has been terminated. This is the recurring message each time the MQTT client posts:

/var/lib/gems/2.1.0/gems/mqtt-0.5.0/lib/mqtt/client.rb:581: warning: instance variable @last_packet_id not initialized

Any ideas?

rlugge commented 7 years ago

Could you post more about your code, @teaching-innovation ? Just the one line isn't helpful to me, though someone who maintains the gem may be able to get more out of it.

How are you running it, how are you terminating it, how are you using it, etc etc.

teaching-innovation commented 7 years ago

Hi @rlugge thanks for the follow up: This is a bit of a summary of what I am trying to achieve: https://community.home-assistant.io/t/sound-monitoring-via-web-cam/24262

It works ok if I use nohup to run the script. But if I fun it without I continually get 581: warning seen bellow? Any ideas?

pi@raspberrypi:~ $ 
/var/lib/gems/2.1.0/gems/mqtt-0.5.0/lib/mqtt/packet.rb:150: warning: method redefined; discarding old version=
/var/lib/gems/2.1.0/gems/mqtt-0.5.0/lib/mqtt/packet.rb:322: warning: method redefined; discarding old duplicate
/var/lib/gems/2.1.0/gems/mqtt-0.5.0/lib/mqtt/packet.rb:327: warning: method redefined; discarding old duplicate=
/var/lib/gems/2.1.0/gems/mqtt-0.5.0/lib/mqtt/packet.rb:335: warning: method redefined; discarding old retain
/var/lib/gems/2.1.0/gems/mqtt-0.5.0/lib/mqtt/packet.rb:340: warning: method redefined; discarding old retain=
/var/lib/gems/2.1.0/gems/mqtt-0.5.0/lib/mqtt/packet.rb:348: warning: method redefined; discarding old qos
/var/lib/gems/2.1.0/gems/mqtt-0.5.0/lib/mqtt/packet.rb:353: warning: method redefined; discarding old qos=
/var/lib/gems/2.1.0/gems/mqtt-0.5.0/lib/mqtt/packet.rb:598: warning: method redefined; discarding old session_present
/var/lib/gems/2.1.0/gems/mqtt-0.5.0/lib/mqtt/packet.rb:603: warning: method redefined; discarding old session_present=
/var/lib/gems/2.1.0/gems/mqtt-0.5.0/lib/mqtt/packet.rb:793: warning: method redefined; discarding old topics=
/var/lib/gems/2.1.0/gems/mqtt-0.5.0/lib/mqtt/packet.rb:882: warning: method redefined; discarding old return_codes=
/var/lib/gems/2.1.0/gems/mqtt-0.5.0/lib/mqtt/packet.rb:947: warning: method redefined; discarding old topics=
/var/lib/gems/2.1.0/gems/mqtt-0.5.0/lib/mqtt/client.rb:581: warning: instance variable @last_packet_id not initialized
/var/lib/gems/2.1.0/gems/mqtt-0.5.0/lib/mqtt/client.rb:581: warning: instance variable @last_packet_id not initialized
/var/lib/gems/2.1.0/gems/mqtt-0.5.0/lib/mqtt/client.rb:581: warning: instance variable @last_packet_id not initialized
/var/lib/gems/2.1.0/gems/mqtt-0.5.0/lib/mqtt/client.rb:581: warning: instance variable @last_packet_id not initialized
/var/lib/gems/2.1.0/gems/mqtt-0.5.0/lib/mqtt/client.rb:581: warning: instance variable @last_packet_id not initialized
/var/lib/gems/2.1.0/gems/mqtt-0.5.0/lib/mqtt/client.rb:581: warning: instance variable @last_packet_id not initialized
rlugge commented 7 years ago

Could you please format that a little cleaner? It's hard to read all jammed together on one line like that. Use triple backticks offset by line returns to make a code block.

teaching-innovation commented 7 years ago

Hi @rlugge, I thought i did put it in a code block, but now that is has triple backticks it looks better.

rlugge commented 7 years ago

@teaching-innovation not really germane to this forum, but typically single backticks are used for small, inline code snippets, while the triple are used for larger code blocks, that's why you had formatting issues the first time -- it was the code format intended for inline use.

As for your issues... are you redefining methods? No clue what could be causing it, to be honest.