mirror of
https://github.com/puma/puma.git
synced 2022-11-09 13:48:40 -05:00
Refactor reaping dead threads. Slight change to license header to make it clearer.
git-svn-id: svn+ssh://rubyforge.org/var/svn/mongrel/trunk@255 19e92222-5c0b-0410-8929-a290d50e31e9
This commit is contained in:
parent
0095a939c3
commit
a6d80703cb
25 changed files with 93 additions and 80 deletions
|
@ -1,4 +1,4 @@
|
|||
/* Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
/* Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
*
|
||||
* Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#line 1 "ext/http11/http11_parser.rl"
|
||||
/* Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
/* Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
*
|
||||
* Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
/* Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
*
|
||||
* Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
/* Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
*
|
||||
* Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
# Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
#
|
||||
# Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
#
|
||||
|
@ -212,7 +212,7 @@ module Mongrel
|
|||
clen -= @body.write(@socket.read(clen % Const::CHUNK_SIZE))
|
||||
|
||||
# then stream out nothing but perfectly sized chunks
|
||||
while clen > 0
|
||||
while clen > 0 and !@socket.closed?
|
||||
data = @socket.read(Const::CHUNK_SIZE)
|
||||
# have to do it this way since @socket.eof? causes it to block
|
||||
raise "Socket closed or read failure" if not data or data.length != Const::CHUNK_SIZE
|
||||
|
@ -224,7 +224,7 @@ module Mongrel
|
|||
@body.rewind
|
||||
rescue Object
|
||||
# any errors means we should delete the file, including if the file is dumped
|
||||
STDERR.puts "Error reading request: #$!"
|
||||
@socket.close unless @socket.closed?
|
||||
@body.delete if @body.class == Tempfile
|
||||
@body = nil # signals that there was a problem
|
||||
end
|
||||
|
@ -454,7 +454,7 @@ module Mongrel
|
|||
|
||||
end
|
||||
|
||||
# This is the main driver of Mongrel, while the Mognrel::HttpParser and Mongrel::URIClassifier
|
||||
# This is the main driver of Mongrel, while the Mongrel::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.
|
||||
|
@ -517,7 +517,7 @@ module Mongrel
|
|||
begin
|
||||
parser = HttpParser.new
|
||||
params = {}
|
||||
|
||||
request = nil
|
||||
data = client.readpartial(Const::CHUNK_SIZE)
|
||||
nparsed = 0
|
||||
|
||||
|
@ -535,6 +535,8 @@ module Mongrel
|
|||
params[Const::PATH_INFO] = path_info
|
||||
params[Const::SCRIPT_NAME] = script_name
|
||||
params[Const::REMOTE_ADDR] = params[Const::HTTP_X_FORWARDED_FOR] || client.peeraddr.last
|
||||
|
||||
# TODO: Find a faster/better way to carve out the range, preferably without copying.
|
||||
data = data[nparsed ... data.length] || ""
|
||||
|
||||
if handlers[0].request_notify
|
||||
|
@ -542,7 +544,6 @@ module Mongrel
|
|||
handlers[0].request_begins(params)
|
||||
end
|
||||
|
||||
# TODO: Find a faster/better way to carve out the range, preferably without copying.
|
||||
request = HttpRequest.new(params, data, client)
|
||||
|
||||
# in the case of large file uploads the user could close the socket, so skip those requests
|
||||
|
@ -563,7 +564,6 @@ module Mongrel
|
|||
end
|
||||
else
|
||||
# Didn't find it, return a stock 404 response.
|
||||
# TODO: Implement customer 404 files (but really they should use a real web server).
|
||||
client.write(Const::ERROR_404_RESPONSE)
|
||||
end
|
||||
|
||||
|
@ -580,27 +580,44 @@ module Mongrel
|
|||
# ignored
|
||||
rescue HttpParserError
|
||||
STDERR.puts "#{Time.now}: BAD CLIENT (#{params[Const::HTTP_X_FORWARDED_FOR] || client.peeraddr.last}): #$!"
|
||||
rescue => details
|
||||
rescue Object
|
||||
STDERR.puts "#{Time.now}: ERROR: #$!"
|
||||
STDERR.puts details.backtrace.join("\n")
|
||||
ensure
|
||||
client.close unless client.closed?
|
||||
request.body.delete if request and request.body.class == Tempfile
|
||||
end
|
||||
end
|
||||
|
||||
# Used internally to kill off any worker threads that have taken too long
|
||||
# to complete processing. Only called if there are too many processors
|
||||
# currently servicing.
|
||||
def reap_dead_workers(worker_list)
|
||||
mark = Time.now
|
||||
worker_list.each do |w|
|
||||
w[:started_on] = Time.now if not w[:started_on]
|
||||
# currently servicing. It returns the count of workers still active
|
||||
# after the reap is done. It only runs if there are workers to reap.
|
||||
def reap_dead_workers(reason='unknown')
|
||||
if @workers.list.length > 0
|
||||
STDERR.puts "#{Time.now}: Reaping #{@workers.list.length} threads for slow workers because of '#{reason}'"
|
||||
mark = Time.now
|
||||
@workers.list.each do |w|
|
||||
w[:started_on] = Time.now if not w[:started_on]
|
||||
|
||||
if mark - w[:started_on] > @death_time + @timeout
|
||||
STDERR.puts "Thread #{w.inspect} is too old, killing."
|
||||
w.raise(StopServer.new("Timed out thread."))
|
||||
if mark - w[:started_on] > @death_time + @timeout
|
||||
STDERR.puts "Thread #{w.inspect} is too old, killing."
|
||||
w.raise(StopServer.new("Timed out thread."))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return @workers.list.length
|
||||
end
|
||||
|
||||
# Performs a wait on all the currently running threads and kills any that take
|
||||
# too long. Right now it just waits 60 seconds, but will expand this to
|
||||
# allow setting. The @timeout setting does extend this waiting period by
|
||||
# that much longer.
|
||||
def graceful_shutdown
|
||||
while reap_dead_workers("shutdown") > 0
|
||||
STDERR.print "Waiting for #{@workers.list.length} requests to finish, could take #{@death_time + @timeout} seconds."
|
||||
sleep @death_time / 10
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
@ -618,14 +635,12 @@ module Mongrel
|
|||
if worker_list.length >= @num_processors
|
||||
STDERR.puts "Server overloaded with #{worker_list.length} processors (#@num_processors max). Dropping connection."
|
||||
client.close
|
||||
reap_dead_workers(worker_list)
|
||||
reap_dead_workers("max processors")
|
||||
else
|
||||
thread = Thread.new do
|
||||
process_client(client)
|
||||
end
|
||||
thread = Thread.new { process_client(client) }
|
||||
|
||||
thread.abort_on_exception = true
|
||||
thread[:started_on] = Time.now
|
||||
thread.priority=1
|
||||
@workers.add(thread)
|
||||
|
||||
sleep @timeout/100 if @timeout > 0
|
||||
|
@ -634,22 +649,12 @@ module Mongrel
|
|||
@socket.close if not @socket.closed?
|
||||
break
|
||||
rescue Errno::EMFILE
|
||||
STDERR.puts "Too many open files. Try increasing ulimits."
|
||||
reap_dead_workers("too many open files")
|
||||
sleep 0.5
|
||||
end
|
||||
end
|
||||
|
||||
# troll through the threads that are waiting and kill any that take too long
|
||||
# TODO: Allow for death time to be set if people ask for it.
|
||||
@death_time = 10
|
||||
shutdown_start = Time.now
|
||||
|
||||
while @workers.list.length > 0
|
||||
waited_for = (Time.now - shutdown_start).ceil
|
||||
STDERR.print "Shutdown waited #{waited_for} for #{@workers.list.length} requests, could take #{@death_time + @timeout} seconds.\r" if @workers.list.length > 0
|
||||
sleep 1
|
||||
reap_dead_workers(@workers.list)
|
||||
end
|
||||
graceful_shutdown
|
||||
end
|
||||
|
||||
return @acceptor
|
||||
|
@ -972,7 +977,7 @@ module Mongrel
|
|||
|
||||
|
||||
# This method should actually be called *outside* of the
|
||||
# Configurator block so that you can control it. In otherwords
|
||||
# Configurator block so that you can control it. In other words
|
||||
# do it like: config.join.
|
||||
def join
|
||||
@listeners.values.each {|s| s.acceptor.join }
|
||||
|
@ -991,9 +996,9 @@ module Mongrel
|
|||
# debug "/", what = [:rails]
|
||||
#
|
||||
# And it will only produce the log/mongrel_debug/rails.log file.
|
||||
# Available options are: :object, :railes, :files, :threads, :params
|
||||
# Available options are: :object, :rails, :files, :threads, :params
|
||||
#
|
||||
# NOTE: Use [:files] to get acccesses dumped to stderr like with WEBrick.
|
||||
# NOTE: Use [:files] to get accesses dumped to stderr like with WEBrick.
|
||||
def debug(location, what = [:object, :rails, :files, :threads, :params])
|
||||
require 'mongrel/debug'
|
||||
handlers = {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
# Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
#
|
||||
# Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
#
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
# Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
#
|
||||
# Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
#
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
# Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
#
|
||||
# Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
#
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
# Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
#
|
||||
# Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
#
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
require 'mongrel/stats'
|
||||
require 'zlib'
|
||||
|
||||
# Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
# Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
#
|
||||
# Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
#
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
# Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
#
|
||||
# Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
#
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
# Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
#
|
||||
# Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
#
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
# Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
#
|
||||
# Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
#
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
# Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
#
|
||||
# Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
#
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
# Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
#
|
||||
# Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
#
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
# Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
#
|
||||
# Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
#
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
# Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
#
|
||||
# Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
#
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
# Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
#
|
||||
# Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
#
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
# Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
#
|
||||
# Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
#
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
# Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
#
|
||||
# Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
#
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
# Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
#
|
||||
# Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
#
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
# Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
#
|
||||
# Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
#
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
# Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
#
|
||||
# Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
#
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mongrel Web Server - A Mostly Ruby Webserver and Library
|
||||
# Mongrel Web Server - A Mostly Ruby HTTP server and Library
|
||||
#
|
||||
# Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
|
||||
#
|
||||
|
|
|
@ -4,34 +4,42 @@ require 'stringio'
|
|||
def do_test(st, chunk)
|
||||
s = TCPSocket.new('127.0.0.1',ARGV[0].to_i);
|
||||
req = StringIO.new(st)
|
||||
|
||||
while data = req.read(chunk)
|
||||
puts "write #{data.length}: '#{data}'"
|
||||
s.write(data)
|
||||
s.flush
|
||||
sleep 0.1
|
||||
nout = 0
|
||||
randstop = rand(st.length / 10)
|
||||
STDERR.puts "stopping after: #{randstop}"
|
||||
|
||||
begin
|
||||
while data = req.read(chunk)
|
||||
nout += s.write(data)
|
||||
s.flush
|
||||
sleep 0.1
|
||||
if nout > randstop
|
||||
STDERR.puts "BANG! after #{nout} bytes."
|
||||
break
|
||||
end
|
||||
end
|
||||
rescue Object
|
||||
STDERR.puts "ERROR: #$!"
|
||||
ensure
|
||||
s.close
|
||||
end
|
||||
s.close
|
||||
end
|
||||
|
||||
content = "-" * (1024 * 240)
|
||||
st = "GET / HTTP/1.1\r\nHost: www.zedshaw.com\r\nContent-Type: text/plain\r\nContent-Length: #{content.length}\r\n\r\n#{content}"
|
||||
|
||||
st = "GET / HTTP/1.1\r\nHost: www.zedshaw.com\r\nContent-Type: text/plain\r\n\r\n"
|
||||
puts "length: #{content.length}"
|
||||
|
||||
threads = []
|
||||
ARGV[1].to_i.times do
|
||||
threads << Thread.new do
|
||||
(st.length - 1).times do |chunk|
|
||||
puts ">>>> #{chunk+1} sized chunks"
|
||||
do_test(st, chunk+1)
|
||||
end
|
||||
|
||||
1000.times do
|
||||
do_test(st, rand(st.length) + 1)
|
||||
end
|
||||
|
||||
t = Thread.new do
|
||||
size = 100
|
||||
puts ">>>> #{size} sized chunks"
|
||||
do_test(st, size)
|
||||
end
|
||||
|
||||
sleep(1+rand)
|
||||
t.abort_on_exception = true
|
||||
threads << t
|
||||
end
|
||||
|
||||
threads.each {|t| t.join}
|
||||
threads.each {|t| t.join}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue