Daemonic
Daemonic is a tool that let's you turn your long running processes into daemons.
At a glance:
- Designed to be external to and independent of your app.
- Possibility to spawn multiple instances of your app.
- Smart restart behavior, comparable to Unicorn.
- Automatically restarts crashed instances of your app.
What can Daemonic do for me?
Say you have written your own daemon. It processes messages of a queue for
example. You can start it by running ruby work.rb
. Now the only thing you
need to do is turn it into a proper daemon. This means daemonizing your
process, managing restarts, adding concurrency, managing a pid file, etc.
This is a lot of work, quite error prone and frankly, very tedious. That's where Daemonic comes in. All you need to do, to start your message processor is:
$ daemonic --command "ruby work.rb" --pid work.pid --workers 5 --daemonize
And voila: you now have 5 instances of your worker, each in it's own process.
This works just like Unicorn, but with any kind of process, not just Rack apps.
You can restart all workers by sending the USR2
signal, just like in Unicorn
too.
$ kill -USR2 `cat work.pid`
This will restart each process individually, meaning you never lose capacity.
There are some caveats though. For one, daemonic checks if the process is running, but it is very naive. It doesn't really know if the app is ready, just if it is running or not.
Secondly, if your app needs a shared resource, spawning multiple workers will not work. This is the case with web servers that need to bind to a port.
Also, your own worker needs to respond to the TERM
and HUP
signals.
Daemonic expects the app to shut down after sending the TERM
signal. It's up
to you to finish up what you are doing.
Installation
You don't need to add daemonic to your Gemfile. You can simply install it and use it straight away.
gem install daemonic
Usage
Daemonic accepts command line options and can also read from a configuration file. The configuration file contains the same options as you would pass to the command line. Here is an example:
--command "ruby work.rb"
--workers 5
--pid tmp/worker.pid
--name my-worker
--log log/development.log
--daemonize
Then you can run Daemonic with the config
option:
$ daemonic --config my-worker.conf
There are a bunch more options. See daemonic --help
for the full list.
Signals
These are the signals daemonic responds to:
TERM
/INT
shuts down all workers and then the masterHUP
will be forwarded to each worker and the master will reload the config fileUSR2
will restart each worker, but not the masterTTIN
increase the number of workers by oneTTOU
decrease the number of workers by one
Creating a worker
Here's an example of a basic daemonic worker:
exiting = false
trap("TERM") { exiting = true }
trap("INT") { exiting = true }
trap("HUP") {
# reload settings
}
puts "Booting worker number #{ENV["DAEMON_WORKER_NUMBER"]}"
SomePollingService.poll do
# do your work here
break if exiting
end
puts "Shutting down worker number #{ENV["DAEMON_WORKER_NUMBER"]}"
Most importantly:
- Trap INT and TERM signals to cleanly stop your process
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request
TODO
- Ability to create init script like behavior.