Class: Raptor::CLI
- Inherits:
-
Object
- Object
- Raptor::CLI
- Defined in:
- lib/raptor/cli.rb,
sig/generated/raptor/cli.rbs
Overview
Command-line interface for the Raptor web server.
CLI parses command-line arguments and starts the server cluster with the specified configuration options. It supports configuring the number of workers, ractors, threads, bind addresses, and various client timeout settings.
Constant Summary collapse
- DEFAULT_WORKER_COUNT =
Etc.nprocessors
- NESTED_OPTION_KEYS =
[:connection, :http1, :http2].freeze
- DEFAULT_OPTIONS =
{ binds: ["tcp://0.0.0.0:9292"].freeze, socket_backlog: 1024, drain_accept_queue: false, workers: DEFAULT_WORKER_COUNT, ractors: 1, threads: 3, rackup: "config.ru", chdir: nil, environment: nil, connection: { first_data_timeout: 30, chunk_data_timeout: 10, write_timeout: 5, max_body_size: nil, body_spool_threshold: 1024 * 1024, }, http1: { persistent_data_timeout: 65, max_keepalive_requests: 100, }, http2: { max_concurrent_streams: 100, }, worker_boot_timeout: 60, worker_timeout: 60, worker_drain_timeout: 25, worker_shutdown_timeout: 30, refork_after: (RUBY_PLATFORM.include?("linux") ? 1000 : nil), before_fork: [].freeze, before_worker_boot: [].freeze, before_worker_shutdown: [].freeze, before_refork: [].freeze, stats_file: "tmp/raptor.json", pid_file: nil, stdout_file: nil, stderr_file: nil, access_log_file: nil, }.freeze
- DEFAULT_CONFIG_PATHS =
["raptor.rb", "config/raptor.rb"].freeze
Class Method Summary collapse
-
.default_config_path(root = Dir.pwd) ⇒ String?
Returns the first existing path in DEFAULT_CONFIG_PATHS resolved against
root, or nil if none exist. -
.load_config_file(path) ⇒ Hash{Symbol => untyped}
Loads a Ruby config file and returns the options hash it evaluates to.
Instance Method Summary collapse
-
#apply_config_file(path) ⇒ void
Loads a config file and merges it into
@optionsover the defaults. -
#create_parser ⇒ OptionParser
Creates the OptionParser instance with all supported command-line options.
-
#extract_config_path(argv) ⇒ String?
Returns the path from a
-c/--configflag inargv, recognising all four OptionParser-accepted forms (-c PATH,-cPATH,--config PATH,--config=PATH). -
#initialize(argv) ⇒ CLI
constructor
Creates a new CLI instance from
argv. -
#run ⇒ void
Runs the requested command.
-
#run_stats ⇒ void
Reads and prints the stats file.
Constructor Details
#initialize(argv) ⇒ CLI
Creates a new CLI instance from argv. A rackup file may be given
as the first positional argument; every other value is parsed as a
flag.
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/raptor/cli.rb', line 113 def initialize(argv) @options = DEFAULT_OPTIONS.dup NESTED_OPTION_KEYS.each { |key| @options[key] = @options[key].dup } @options[:launch_command] = $PROGRAM_NAME @options[:launch_argv] = argv.dup if argv.first == "stats" argv.shift @command = :stats else @command = :server end apply_config_file(extract_config_path(argv) || self.class.default_config_path) @parser = create_parser @parser.parse!(argv) @options[:rackup] = argv.first if @command == :server && argv.first end |
Class Method Details
.default_config_path(root = Dir.pwd) ⇒ String?
Returns the first existing path in DEFAULT_CONFIG_PATHS resolved
against root, or nil if none exist.
96 97 98 |
# File 'lib/raptor/cli.rb', line 96 def self.default_config_path(root = Dir.pwd) DEFAULT_CONFIG_PATHS.find { |path| File.exist?(File.join(root, path)) } end |
.load_config_file(path) ⇒ Hash{Symbol => untyped}
Loads a Ruby config file and returns the options hash it evaluates
to. Evaluated at the top level so Raptor::* constants resolve the
same as in a regular script.
82 83 84 85 86 87 |
# File 'lib/raptor/cli.rb', line 82 def self.load_config_file(path) config = eval(File.read(path), TOPLEVEL_BINDING, path, 1) raise ArgumentError, "Config file at #{path.inspect} must return a Hash, got #{config.class}" unless config.is_a?(Hash) config end |
Instance Method Details
#apply_config_file(path) ⇒ void
This method returns an undefined value.
Loads a config file and merges it into @options over the defaults.
194 195 196 197 198 199 200 201 202 203 204 205 |
# File 'lib/raptor/cli.rb', line 194 def apply_config_file(path) return unless path config = self.class.load_config_file(path) config.each do |key, value| if NESTED_OPTION_KEYS.include?(key) && value.is_a?(Hash) @options[key] = @options[key].merge(value) else @options[key] = value end end end |
#create_parser ⇒ OptionParser
Creates the OptionParser instance with all supported command-line options.
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 |
# File 'lib/raptor/cli.rb', line 212 def create_parser OptionParser.new do |opts| opts. = "Usage: raptor [options] [rackup file]" opts.on("-c", "--config PATH", String, "Load configuration from PATH") do # Loaded in #initialize before parsing so CLI args can override config values end opts.on("-b", "--bind URI", String, "Bind address (default: tcp://0.0.0.0:9292)") do |bind| if @options[:binds].equal?(DEFAULT_OPTIONS[:binds]) @options[:binds] = [bind] else @options[:binds] << bind end end opts.on("--socket-backlog NUM", Integer, "Socket listen backlog (default: 1024)") do |num| @options[:socket_backlog] = num end opts.on("--[no-]drain-accept-queue", "Drain the kernel accept queue on shutdown (default: off)") do |bool| @options[:drain_accept_queue] = bool end opts.on("-w", "--workers NUM", Integer, "Number of worker processes (default: #{DEFAULT_WORKER_COUNT})") do |num| @options[:workers] = num end opts.on("-r", "--ractors NUM", Integer, "Number of pipeline ractors per worker (default: 1)") do |num| @options[:ractors] = num end opts.on("-t", "--threads NUM", Integer, "Number of application threads per worker (default: 3)") do |num| @options[:threads] = num end opts.on("-C", "--chdir PATH", String, "Change to PATH before loading the Rack application (default: none)") do |path| @options[:chdir] = path end opts.on("-e", "--environment ENV", String, "Application environment label; falls back to $RAILS_ENV, then $RACK_ENV, then development") do |env| @options[:environment] = env end opts.on("--first-data-timeout SECONDS", Integer, "First data timeout in seconds (default: 30)") do |timeout| @options[:connection][:first_data_timeout] = timeout end opts.on("--chunk-data-timeout SECONDS", Integer, "Chunk data timeout in seconds (default: 10)") do |timeout| @options[:connection][:chunk_data_timeout] = timeout end opts.on("--write-timeout SECONDS", Integer, "Per-write socket timeout in seconds (default: 5)") do |timeout| @options[:connection][:write_timeout] = timeout end opts.on("--max-body-size BYTES", Integer, "Maximum request body size in bytes (default: unlimited)") do |bytes| @options[:connection][:max_body_size] = bytes end opts.on("--body-spool-threshold BYTES", Integer, "Request body spool threshold in bytes (default: #{1024 * 1024})") do |bytes| @options[:connection][:body_spool_threshold] = bytes end opts.on("--http1-persistent-data-timeout SECONDS", Integer, "HTTP/1.1 keep-alive idle timeout in seconds (default: 65)") do |timeout| @options[:http1][:persistent_data_timeout] = timeout end opts.on("--http1-max-keepalive-requests NUM", Integer, "Maximum HTTP/1.1 requests per keep-alive connection (default: 100)") do |num| @options[:http1][:max_keepalive_requests] = num end opts.on("--http2-max-concurrent-streams NUM", Integer, "Maximum HTTP/2 concurrent streams per connection (default: 100)") do |num| @options[:http2][:max_concurrent_streams] = num end opts.on("--worker-boot-timeout SECONDS", Integer, "Worker boot timeout in seconds (default: 60)") do |timeout| @options[:worker_boot_timeout] = timeout end opts.on("--worker-timeout SECONDS", Integer, "Worker check-in timeout in seconds (default: 60)") do |timeout| @options[:worker_timeout] = timeout end opts.on("--worker-drain-timeout SECONDS", Integer, "Worker request-drain timeout in seconds (default: 25)") do |timeout| @options[:worker_drain_timeout] = timeout end opts.on("--worker-shutdown-timeout SECONDS", Integer, "Worker shutdown timeout in seconds (default: 30)") do |timeout| @options[:worker_shutdown_timeout] = timeout end opts.on("--refork-after NUM", Integer, "Refork workers from a warmed source after any worker crosses NUM requests; 0 disables (default: 1000)") do |num| @options[:refork_after] = num end opts.on("--stats-file PATH", String, "Stats file path (default: tmp/raptor.json)") do |path| @options[:stats_file] = path end opts.on("--pid-file PATH", String, "PID file path (default: none)") do |path| @options[:pid_file] = path end opts.on("--stdout-file PATH", String, "Redirect stdout to PATH; reopened on SIGHUP (default: none)") do |path| @options[:stdout_file] = path end opts.on("--stderr-file PATH", String, "Redirect stderr to PATH; reopened on SIGHUP (default: none)") do |path| @options[:stderr_file] = path end opts.on("--access-log-file PATH", String, "Write Common Log Format access logs to PATH; reopened on SIGHUP (default: none)") do |path| @options[:access_log_file] = path end opts.on("--help", "Show this help") do puts opts exit end opts.on("-v", "--version", "Show version") do puts Raptor::VERSION exit end end end |
#extract_config_path(argv) ⇒ String?
Returns the path from a -c/--config flag in argv, recognising
all four OptionParser-accepted forms (-c PATH, -cPATH,
--config PATH, --config=PATH).
178 179 180 181 182 183 184 185 186 |
# File 'lib/raptor/cli.rb', line 178 def extract_config_path(argv) argv.each_with_index do |arg, index| case arg when "-c", "--config" then return argv[index + 1] when /\A--config=(.*)\z/, /\A-c(.+)\z/ then return Regexp.last_match(1) end end nil end |
#run ⇒ void
This method returns an undefined value.
Runs the requested command.
139 140 141 |
# File 'lib/raptor/cli.rb', line 139 def run @command == :stats ? run_stats : Cluster.run(@options) end |
#run_stats ⇒ void
This method returns an undefined value.
Reads and prints the stats file.
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/raptor/cli.rb', line 150 def run_stats stats_file = @options[:stats_file] unless File.exist?(stats_file) warn "No stats file at #{stats_file.inspect}. Is Raptor running?" exit 1 end data = JSON.parse(File.read(stats_file), symbolize_names: true) puts "Master PID: #{data[:master_pid]}" data[:workers].each do |worker| status = worker[:booted] ? "booted" : "starting" last_checkin = Time.at(worker[:last_checkin]).strftime("%H:%M:%S") puts "Worker #{worker[:index]} (phase #{worker[:phase]}): pid=#{worker[:pid]}, requests=#{worker[:requests]}, " \ "busy=#{worker[:busy_threads]}/#{worker[:thread_capacity]}, backlog=#{worker[:backlog]}, " \ "#{status}, last_checkin=#{last_checkin}" end end |