Class: Mongrel::HttpServer
- Inherits:
-
Object
- Object
- Mongrel::HttpServer
- Defined in:
- lib/mongrel.rb
Overview
This is the main driver of Mongrel, while the Mognrel::HttpParser and Mongrel::URIClassifier make up the majority of how the server functions. It’s a very simple class that just has a thread accepting connections and a simple HttpServer.process_client function to do the heavy lifting with the IO and Ruby.
You use it by doing the following:
server = HttpServer.new("0.0.0.0", 3000)
server.register("/stuff", MyNifterHandler.new)
server.run.join
The last line can be just server.run if you don’t want to join the thread used. If you don’t though Ruby will mysteriously just exit on you.
Ruby’s thread implementation is “interesting” to say the least. Experiments with many different types of IO processing simply cannot make a dent in it. Future releases of Mongrel will find other creative ways to make threads faster, but don’t hold your breath until Ruby 1.9 is actually finally useful.
Instance Attribute Summary collapse
-
#acceptor ⇒ Object
readonly
Returns the value of attribute acceptor.
Instance Method Summary collapse
-
#initialize(host, port, num_processors = 20, timeout = 120) ⇒ HttpServer
constructor
Creates a working server on host:port (strange things happen if port isn’t a Number).
-
#process_client(client) ⇒ Object
Does the majority of the IO processing.
-
#register(uri, handler) ⇒ Object
Simply registers a handler with the internal URIClassifier.
-
#run ⇒ Object
Runs the thing.
-
#stop ⇒ Object
Stops the acceptor thread and then causes the worker threads to finish off the request queue before finally exiting.
-
#unregister(uri) ⇒ Object
Removes any handler registered at the given URI.
Constructor Details
#initialize(host, port, num_processors = 20, timeout = 120) ⇒ HttpServer
Creates a working server on host:port (strange things happen if port isn’t a Number). Use HttpServer::run to start the server.
The num_processors variable has varying affects on how requests are processed. You’d think adding more processing threads (processors) would make the server faster, but that’s just not true. There’s actually an effect of how Ruby does threads such that the more processors waiting on the request queue, the slower the system is to handle each request. But, the lower the number of processors the fewer concurrent responses the server can make.
20 is the default number of processors and is based on experimentation on a few systems. If you find that you overload Mongrel too much try changing it higher. If you find that responses are way too slow try lowering it (after you’ve tuned your stuff of course).
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 |
# File 'lib/mongrel.rb', line 345 def initialize(host, port, num_processors=20, timeout=120) @socket = TCPServer.new(host, port) @classifier = URIClassifier.new @req_queue = Queue.new @host = host @port = port @processors = [] # create the worker threads num_processors.times do |i| @processors << Thread.new do while client = @req_queue.deq Timeout::timeout(timeout) do process_client(client) end end end end end |
Instance Attribute Details
#acceptor ⇒ Object (readonly)
Returns the value of attribute acceptor.
329 330 331 |
# File 'lib/mongrel.rb', line 329 def acceptor @acceptor end |
Instance Method Details
#process_client(client) ⇒ Object
Does the majority of the IO processing. It has been written in Ruby using about 7 different IO processing strategies and no matter how it’s done the performance just does not improve. It is currently carefully constructed to make sure that it gets the best possible performance, but anyone who thinks they can make it faster is more than welcome to take a crack at it.
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 |
# File 'lib/mongrel.rb', line 373 def process_client(client) begin parser = HttpParser.new params = {} data = client.readpartial(Const::CHUNK_SIZE) while true nread = parser.execute(params, data) if parser.finished? script_name, path_info, handler = @classifier.resolve(params[Const::REQUEST_URI]) if handler params[Const::PATH_INFO] = path_info params[Const::SCRIPT_NAME] = script_name request = HttpRequest.new(params, data[nread ... data.length], client) response = HttpResponse.new(client) handler.process(request, response) else client.write(Const::ERROR_404_RESPONSE) end break #done else # gotta stream and read again until we can get the parser to be character safe # TODO: make this more efficient since this means we're parsing a lot repeatedly parser.reset data << client.readpartial(Const::CHUNK_SIZE) end end rescue EOFError # ignored rescue Errno::ECONNRESET # ignored rescue Errno::EPIPE # ignored rescue => details STDERR.puts "ERROR(#{details.class}): #{details}" STDERR.puts details.backtrace.join("\n") ensure client.close end end |
#register(uri, handler) ⇒ Object
Simply registers a handler with the internal URIClassifier. When the URI is found in the prefix of a request then your handler’s HttpHandler::process method is called. See Mongrel::URIClassifier#register for more information.
456 457 458 |
# File 'lib/mongrel.rb', line 456 def register(uri, handler) @classifier.register(uri, handler) end |
#run ⇒ Object
Runs the thing. It returns the thread used so you can “join” it. You can also access the HttpServer::acceptor attribute to get the thread later.
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 |
# File 'lib/mongrel.rb', line 418 def run BasicSocket.do_not_reverse_lookup=true @acceptor = Thread.new do Thread.current[:stopped] = false while not Thread.current[:stopped] begin @req_queue << @socket.accept rescue StopServer STDERR.puts "Server stopped. Exiting." @socket.close if not @socket.closed? break rescue Errno::EMFILE STDERR.puts "Too many open files. Try increasing ulimits." sleep 0.5 end end # now that processing is done we feed enough false onto the request queue to get # each processor to exit and stop processing. @processors.length.times { @req_queue << false } # finally we wait until the queue is empty while @req_queue.length > 0 STDERR.puts "Shutdown waiting for #{@req_queue.length} requests" if @req_queue.length > 0 sleep 1 end end @acceptor.priority = 1 return @acceptor end |
#stop ⇒ Object
Stops the acceptor thread and then causes the worker threads to finish off the request queue before finally exiting.
468 469 470 471 472 473 474 475 |
# File 'lib/mongrel.rb', line 468 def stop stopper = Thread.new do @acceptor[:stopped] = true exc = StopServer.new @acceptor.raise(exc) end stopper.priority = 10 end |
#unregister(uri) ⇒ Object
Removes any handler registered at the given URI. See Mongrel::URIClassifier#unregister for more information.
462 463 464 |
# File 'lib/mongrel.rb', line 462 def unregister(uri) @classifier.unregister(uri) end |