class Merb::Rack::AbstractAdapter
Public Class Methods
This method is designed to be overridden in a rack adapter. It will be called to create a new instance of the server for the adapter to start. The adapter should attempt to bind to a port at this point. This is called from the AbstractAdapter start method.
Parameters¶ ↑
- port<Integer>
-
The port the server should listen on
:api: plugin @overridable
# File lib/merb-core/rack/adapter/abstract.rb, line 68 def self.new_server(port) raise NotImplemented end
Set the process title.
Parameters¶ ↑
- whoami<Symbol>
-
Either :spawner for the master process or :worker for any of the worker
processes.
- port<Integer>
-
The base port that the app is running on.
:api: private
# File lib/merb-core/rack/adapter/abstract.rb, line 326 def self.process_title(whoami, port) name = Merb::Config[:name] app = "merb#{" : #{name}" if (name && name != "merb")}" max_port = Merb::Config[:cluster] ? (Merb::Config[:cluster] - 1) : 0 numbers = ((whoami != :worker) && (max_port > 0)) ? "#{port}..#{port + max_port}" : port file = Merb::Config[:socket_file] % port if Merb::Config[:socket_file] listening_on = if Merb::Config[:socket] "socket#{'s' if max_port > 0 && whoami != :worker} #{numbers} " "#{file ? file : "#{Merb.log_path}/#{name}.#{port}.sock"}" else "port#{'s' if max_port > 0 && whoami != :worker} #{port}" end "#{app} : #{whoami} (#{listening_on})" end
Spawn a new worker process at a port.
Parameters¶ ↑
- port<Integer>
-
The port to start the worker process on.
:api: private
# File lib/merb-core/rack/adapter/abstract.rb, line 93 def self.spawn_worker(port) worker_pid = Kernel.fork # If we have a worker_pid, we're in the parent. if worker_pid.nil? # Seed the random number generator Kernel.srand # Restart the run_later worker, unless we're in the parent or it's alive. Merb::Worker.restart unless Merb::Worker.alive? # Spawn the worker start_at_port(port, @opts) throw(:new_worker) end @pids[port] = worker_pid $WORKERS = @pids.values end
The main start method for bootloaders that support forking. This method launches the adapters which inherit using the ::new_server and ::start_server methods. This method should not be overridden in adapters which want to fork.
Parameters¶ ↑
- opts<Hash>
-
A hash of options
socket: the socket to bind to port: the port to bind to cluster: the number
:api: private
# File lib/merb-core/rack/adapter/abstract.rb, line 126 def self.start(opts={}) @opts = opts $WORKERS ||= [] parent = nil @pids = {} port = (opts[:socket] || opts[:port]).to_i max_port = Merb::Config[:cluster] ? Merb::Config[:cluster] - 1 : 0 # If we only have a single merb, just start it up and dispense with # the spawner/worker setup. if max_port == 0 start_at_port(port) return end $0 = process_title(:spawner, port) # For each port, spawn a new worker. The parent will continue in # the loop, while the worker will throw :new_worker and be booted # out of the loop. catch(:new_worker) do 0.upto(max_port) do |i| parent = spawn_worker(port + i) end end # If we're in a worker, we're done. Otherwise, we've completed # setting up workers and now need to watch them. return unless parent # For each worker, set up a thread in the spawner to watch it 0.upto(max_port) do |i| Thread.new do catch(:new_worker) do loop do pid, status = @pids[port + i], nil poller = Merb::System::PortablePoller.new(pid) begin i = 0 loop do # Watch for the pid to exit. _, status = Process.wait2(pid, Process::WNOHANG) break if status if (i % 120 == 0) && Merb::Config[:max_memory] && poller.memory > Merb::Config[:max_memory] Process.kill("INT", pid) if (Process.kill(0, pid) rescue false) sleep Merb::Config[:hang_time] || 5 Process.kill(9, pid) Process.wait2(pid) if (Process.kill(0, pid) rescue false) end status = Struct.new(:exitstatus).new(nil) break end i += 1 sleep 0.25 end # If the pid doesn't exist, we want to silently exit instead of # raising here. rescue SystemCallError => e ensure # If there was no worker with that PID, the status was non-0 # (we send back a status of 128 when ABRT is called on a # worker, and Merb.fatal! exits with a status of 1), or if # Merb is in the process of exiting, *then* don't respawn. # Note that processes killed with kill -9 will return no # exitstatus, and we respawn them. if !status || (status.exitstatus && status.exitstatus != 0) || Merb.exiting then Thread.exit end end # Otherwise, respawn the worker, and watch it again. spawn_worker(port + i) end end end end # The spawner process will make it here, and when it does, it should just # sleep so it can pick up ctrl-c if it's in console mode. sleep end
Fork a server on the specified port and start the app.
Parameters¶ ↑
- port<Integer>
-
The port to start the server on
- opts<Hash>
-
The hash of options, defaults to the @opts
instance variable.
:api: private
# File lib/merb-core/rack/adapter/abstract.rb, line 224 def self.start_at_port(port, opts = @opts) at_exit do Merb::Server.remove_pid(port) end # If Merb is daemonized, trap INT. If it's not daemonized, # we let the master process' ctrl-c control the cluster # of workers. if Merb::Config[:daemonize] Merb.trap('INT') do Merb.exiting = true stop Merb.logger.warn! "Exiting port #{port}\n" exit_process end # If it was not fork_for_class_load, we already set up # ctrl-c handlers in the master thread. elsif Merb::Config[:fork_for_class_load] if Merb::Config[:console_trap] Merb::Server.add_irb_trap end end # In daemonized mode or not, support HUPing the process to # restart it. Merb.trap('HUP') do Merb.exiting = true stop Merb.logger.warn! "Exiting port #{port} on #{Process.pid}\n" exit_process end # ABRTing the process will kill it, and it will not be respawned. Merb.trap('ABRT') do Merb.exiting = true stopped = stop(128) Merb.logger.warn! "Exiting port #{port}\n" if stopped exit_process(128) end # Each worker gets its own `ps' name. $0 = process_title(:worker, port) # Store the PID for this worker Merb::Server.store_pid(port) Merb::Config[:log_delimiter] = "#{process_title(:worker, port)} ~ " Merb.reset_logger! Merb.logger.warn!("Starting #{self.name.split("::").last} at port #{port}") # If we can't connect to the port, keep trying until we can. Print # a warning about this once. Try every 0.25s. printed_warning = false loop do begin # Call the adapter's new_server method, which should attempt # to bind to a port. new_server(port) rescue Errno::EADDRINUSE => e if Merb::Config[:bind_fail_fatal] Merb.fatal! "Could not bind to #{port}. It was already in use", e end unless printed_warning Merb.logger.warn! "Port #{port} is in use, " "Waiting for it to become available." printed_warning = true end sleep 0.25 next end break end Merb.logger.warn! "Successfully bound to port #{port}" Merb::Server.change_privilege # Call the adapter's start_server method. start_server end
This method is designed to be overridden in a rack adapter. It will be called to start a server created with the ::new_server method. This is called from the AbstractAdapter start method.
:api: plugin @overridable
# File lib/merb-core/rack/adapter/abstract.rb, line 54 def self.start_server raise NotImplemented end
This method is designed to be overridden in a rack adapter. It will be called to stop the adapter server.
Parameters¶ ↑
- status<Integer>
-
The exit status the adapter should exit with.
Returns¶ ↑
- Boolean
-
True if the server was properly stopped.
:api: plugin @overridable
# File lib/merb-core/rack/adapter/abstract.rb, line 83 def self.stop(status) raise NotImplemented end