class Servolux::Server


server.startup
server.extend DebugSignal
}
logger.debug “hey look - a debug message”
logger.info “Running @ #{Time.now}”
server = Servolux::Server.new(‘Debugger’, :interval => 2) {
end
end
end
logger.level = :debug
@old_log_level = logger.level
else
@old_log_level = nil
logger.level = @old_log_level
if @old_log_level
@old_log_level ||= nil
def usr1
module DebugSignal
this is a module, it can be used with any Servolux::Server instance.
original log level each time SIGUSR1 is sent to the server process. Since
is sent to the process. The log level toggles between “debug” and the
This example shows how to change the log level of the server when SIGUSR1
=== Signals
== Examples
server.startup
end
end
puts “I’m alive and well @ #{Time.now}”
def run
class << server
server = Servolux::Server.new(‘Singleton’, :interval => 1)
Singleton Class
server.startup
server.extend MyServer
server = Servolux::Server.new(‘Module’, :interval => 1)
end
end
puts “I’m alive and well @ #{Time.now}”
def run
module MyServer
Extension
server.startup
server = MyServer.new(‘MyServer’, :interval => 1)
end
end
puts “I’m alive and well @ #{Time.now}”
def run
class MyServer < Servolux::Server
Inheritance
these methods. In a nutshell:
that Ruby provides for defining methods on objects can be used to define
the run method, signal handlers, and before/after methods. Any pattern
For more complex services you will need to define your own server methods:
server.startup
}
puts “I’m alive and well @ #{Time.now}”
server = Servolux::Server.new(‘Basic’, :interval => 1) {
initializer. This block will be used as the run method.
For simple, quick and dirty servers just pass a block to the Server
== Usage
truly dead.
guaranteed to NOT be called till after the run loop thread is well and
just after the run loop thread has died; the after_stopping method is
before the run loop thread is signaled for shutdown. The second is called
shutdown: before_stopping and after_stopping. The first is called just
Likewise, two other methods are called before and after the run loop is
actually been scheduled).
thread has been created (no guarantee is made that the run loop thread has
is created and started. The second is called just after the run loop
and after_starting. The first is called just before the run loop thread
methods are called before and after the run loop starts: before_starting
There are a few other methods that are useful and should be mentioned. Two
server.
In order to handle SIGUSR1 you would define a usr1 method for your
usr2 SIGUSR2 none
usr1 SIGUSR1 none
term SIGTERM shutdown
int SIGINT shutdown
hup SIGHUP none
——–----------—————-
Method | Signal | Default Action
recognized by the Server class:
(another alias for shutdown). The following signal methods are
alias for shutdown). Likewise, SIGTERM is handled by the term method
server instance. For example, SIGINT is handled by the int method (an
too). A few other signals can be handled by defining a few methods on your
shutdown the server by calling the shutdown method (provided by default,
SIGINT and SIGTERM are handled by default. These signals will gracefully
called within the run loop instead of a run method.
Optionally, you can provide a block to the new method and it will be
provide is a run method that will be called by the server’s run loop.
management, signal handling, run loop, logging, etc. All that you need to
The Server class provides for standard server features: process ID file
== Details
period of time either in the foreground or as a daemon.
Ruby. A server in this context is any process that should run for a long
The Server class makes it simple to create a server-type application in
== Synopsis

def halt_signal_processing

def halt_signal_processing
  if defined?(@wr) && !@wr.nil? && !@wr.closed?
    @queue << :halt
    @wr.write("!")
    @wr.flush
  end
end

def initialize( name, opts = {}, &block )


* interval :: Sleep interval between invocations of the _block_
* pid_file :: Location of the PID file
* logger :: The logger instance this server will use
==== Options

loop at the configured interval.
_options_ hash. The _block_ is run inside a separate thread that will
Creates a new server identified by _name_ and configured from the

Server.new( name, options = {} ) { block }
call-seq:
def initialize( name, opts = {}, &block )
  @name = name
  @mutex = Mutex.new
  @shutdown = nil
  self.logger   = opts.fetch(:logger, Servolux::NullLogger())
  self.interval = opts.fetch(:interval, 0)
  self.pid_file = opts.fetch(:pid_file, name)
  if block
    eg = class << self; self; end
    eg.__send__(:define_method, :run, &block)
  end
  ary = %w[name logger pid_file].map { |var|
    self.send(var).nil? ? var : nil
  }.compact
  raise Error, "These variables are required: #{ary.join(', ')}." unless ary.empty?
end

def pid_file=( value )

Raises an ArgumentError if the `value` cannot be used as a PID file.

value - The PID file name or a PidFile instance.

instance.
it is used. If a name is given, then that name is used to create a PifFile
Set the PID file to the given `value`. If a PidFile instance is given, then
def pid_file=( value )
  @pid_file =
    case value
    when Servolux::PidFile
      value
    when String
      path = File.dirname(value)
      fn = File.basename(value, ".pid")
      Servolux::PidFile.new(:name => fn, :path => path, :logger => logger)
    else
      raise ArgumentError, "#{value.inspect} cannot be used as a PID file"
    end
end

def process_signals

def process_signals
  IO.select([@rd])
  @rd.read_nonblock(42)
  while !@queue.empty?
    method = @queue.shift
    next if method.nil?
    return false if method == :halt
    self.send(method)
  end
  return true
rescue IO::WaitReadable
  return true
rescue IOError, EOFError, Errno::EBADF
  return false
rescue StandardError => err
  logger.error "Exception in signal handler: #{method}"
  logger.error err
  return false
end

def shutdown

Returns:
  • (Server) - self
def shutdown
  return self unless running?
  stop
  @mutex.synchronize {
    @shutdown.signal
    @shutdown = nil
  }
  self
end

def startup( wait = false )

Returns:
  • (Server) - self
def startup( wait = false )
  return self if running?
  @mutex.synchronize {
    @shutdown = ConditionVariable.new
  }
  begin
    trap_signals
    pid_file.write
    start
    join
    wait_for_shutdown if wait
  ensure
    pid_file.delete
    halt_signal_processing
  end
  return self
end

def trap_signals

def trap_signals
  @queue = []
  @rd, @wr = IO.pipe
  SIGNALS.each do |sig|
    method = sig.downcase.to_sym
    if self.respond_to? method
      Signal.trap(sig) do
        begin
          @queue << method
          @wr.write_nonblock("!")
        rescue StandardError => err
          logger.error "Exception in signal handler: #{method}"
          logger.error err
        end
      end
    end
  end
  Thread.new {
    :run while process_signals
    @rd.close if !@rd.nil? && !@rd.closed?
    @wr.close if !@wr.nil? && !@wr.closed?
    logger.info "Signal processing thread has stopped"
  }
end

def wait_for_shutdown


blocked until the server has been safely stopped.
method has been called and has completed. The current thread will be
If the server has been started, this method waits till the +shutdown+
def wait_for_shutdown
  @mutex.synchronize {
    @shutdown.wait(@mutex) unless @shutdown.nil?
  }
  self
end