mirror of
https://github.com/puma/puma.git
synced 2022-11-09 13:48:40 -05:00
Release that improves performance a bit on most platforms, but dramatically on freebsd.
git-svn-id: svn+ssh://rubyforge.org/var/svn/mongrel/trunk@117 19e92222-5c0b-0410-8929-a290d50e31e9
This commit is contained in:
parent
ebeac4b031
commit
6634d89019
6 changed files with 77 additions and 26 deletions
2
Rakefile
2
Rakefile
|
@ -32,7 +32,7 @@ end
|
||||||
setup_extension("http11", "http11")
|
setup_extension("http11", "http11")
|
||||||
|
|
||||||
name="mongrel"
|
name="mongrel"
|
||||||
version="0.3.11"
|
version="0.3.12"
|
||||||
|
|
||||||
setup_gem(name, version) do |spec|
|
setup_gem(name, version) do |spec|
|
||||||
spec.summary = "A small fast HTTP library and server that runs Rails, Camping, and Nitro apps."
|
spec.summary = "A small fast HTTP library and server that runs Rails, Camping, and Nitro apps."
|
||||||
|
|
|
@ -113,6 +113,12 @@ class Start < GemPlugin::Plugin "/commands"
|
||||||
File.unlink @pid_file if File.exist?(@pid_file)
|
File.unlink @pid_file if File.exist?(@pid_file)
|
||||||
@restart = true
|
@restart = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trap("INT") {
|
||||||
|
server.stop
|
||||||
|
File.unlink @pid_file if File.exist?(@pid_file)
|
||||||
|
@restart = false
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
# hook up any rails specific plugins
|
# hook up any rails specific plugins
|
||||||
|
|
|
@ -142,6 +142,40 @@ module Mongrel
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.escape(s)
|
||||||
|
s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
|
||||||
|
'%'+$1.unpack('H2'*$1.size).join('%').upcase
|
||||||
|
}.tr(' ', '+')
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def self.unescape(s)
|
||||||
|
s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
|
||||||
|
[$1.delete('%')].pack('H*')
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def self.query_parse(qs, d = '&;')
|
||||||
|
params = {}
|
||||||
|
(qs||'').split(/[#{d}] */n).inject(params) { |h,p|
|
||||||
|
k, v=unescape(p).split('=',2)
|
||||||
|
if cur = params[k]
|
||||||
|
if cur.class == Array
|
||||||
|
params[k] << v
|
||||||
|
else
|
||||||
|
params[k] = [cur, v]
|
||||||
|
end
|
||||||
|
else
|
||||||
|
params[k] = v
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
return params
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -292,25 +326,11 @@ module Mongrel
|
||||||
# try lowering it (after you've tuned your stuff of course).
|
# try lowering it (after you've tuned your stuff of course).
|
||||||
def initialize(host, port, num_processors=20, timeout=120)
|
def initialize(host, port, num_processors=20, timeout=120)
|
||||||
@socket = TCPServer.new(host, port)
|
@socket = TCPServer.new(host, port)
|
||||||
|
|
||||||
@classifier = URIClassifier.new
|
@classifier = URIClassifier.new
|
||||||
@req_queue = Queue.new
|
|
||||||
@host = host
|
@host = host
|
||||||
@port = port
|
@port = port
|
||||||
@processors = []
|
@worker_group = ThreadGroup.new
|
||||||
|
@timeout = timeout
|
||||||
# create the worker threads
|
|
||||||
num_processors.times do |i|
|
|
||||||
@processors << Thread.new do
|
|
||||||
parser = HttpParser.new
|
|
||||||
while client = @req_queue.deq
|
|
||||||
Timeout::timeout(timeout) do
|
|
||||||
process_client(client, parser)
|
|
||||||
parser.reset
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -320,8 +340,9 @@ module Mongrel
|
||||||
# the performance just does not improve. It is currently carefully constructed
|
# the performance just does not improve. It is currently carefully constructed
|
||||||
# to make sure that it gets the best possible performance, but anyone who
|
# 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.
|
# thinks they can make it faster is more than welcome to take a crack at it.
|
||||||
def process_client(client, parser)
|
def process_client(client)
|
||||||
begin
|
begin
|
||||||
|
parser = HttpParser.new
|
||||||
params = {}
|
params = {}
|
||||||
|
|
||||||
data = client.readpartial(Const::CHUNK_SIZE)
|
data = client.readpartial(Const::CHUNK_SIZE)
|
||||||
|
@ -368,13 +389,22 @@ module Mongrel
|
||||||
# Runs the thing. It returns the thread used so you can "join" it. You can also
|
# 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.
|
# access the HttpServer::acceptor attribute to get the thread later.
|
||||||
def run
|
def run
|
||||||
|
|
||||||
BasicSocket.do_not_reverse_lookup=true
|
BasicSocket.do_not_reverse_lookup=true
|
||||||
|
|
||||||
@acceptor = Thread.new do
|
@acceptor = Thread.new do
|
||||||
Thread.current[:stopped] = false
|
Thread.current[:stopped] = false
|
||||||
|
|
||||||
while not Thread.current[:stopped]
|
while not Thread.current[:stopped]
|
||||||
begin
|
begin
|
||||||
@req_queue << @socket.accept
|
client = @socket.accept
|
||||||
|
|
||||||
|
thread = Thread.new do
|
||||||
|
process_client(client)
|
||||||
|
end
|
||||||
|
|
||||||
|
thread.priority=1
|
||||||
|
@worker_group.add(thread)
|
||||||
rescue StopServer
|
rescue StopServer
|
||||||
STDERR.puts "Server stopped. Exiting."
|
STDERR.puts "Server stopped. Exiting."
|
||||||
@socket.close if not @socket.closed?
|
@socket.close if not @socket.closed?
|
||||||
|
@ -387,17 +417,14 @@ module Mongrel
|
||||||
|
|
||||||
# now that processing is done we feed enough false onto the request queue to get
|
# now that processing is done we feed enough false onto the request queue to get
|
||||||
# each processor to exit and stop processing.
|
# each processor to exit and stop processing.
|
||||||
@processors.length.times { @req_queue << false }
|
|
||||||
|
|
||||||
# finally we wait until the queue is empty
|
# finally we wait until the queue is empty
|
||||||
while @req_queue.length > 0
|
while @worker_group.list.length > 0
|
||||||
STDERR.puts "Shutdown waiting for #{@req_queue.length} requests" if @req_queue.length > 0
|
STDERR.puts "Shutdown waiting for #{@worker_group.list.length} requests" if @worker_group.list.length > 0
|
||||||
sleep 1
|
sleep 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@acceptor.priority = 1
|
|
||||||
|
|
||||||
return @acceptor
|
return @acceptor
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ setup_gem(name, version) do |spec|
|
||||||
spec.description = spec.summary
|
spec.description = spec.summary
|
||||||
spec.author="Zed A. Shaw"
|
spec.author="Zed A. Shaw"
|
||||||
spec.add_dependency('gem_plugin', '>= 0.2.1')
|
spec.add_dependency('gem_plugin', '>= 0.2.1')
|
||||||
spec.add_dependency('mongrel', '>= 0.2.1')
|
spec.add_dependency('mongrel', '>= 0.3.11')
|
||||||
spec.files += Dir.glob("resources/**/*")
|
spec.files += Dir.glob("resources/**/*")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
require 'test/unit'
|
require 'test/unit'
|
||||||
require 'http11'
|
require 'http11'
|
||||||
|
require 'mongrel'
|
||||||
|
require 'benchmark'
|
||||||
|
|
||||||
|
include Mongrel
|
||||||
|
|
||||||
class HttpParserTest < Test::Unit::TestCase
|
class HttpParserTest < Test::Unit::TestCase
|
||||||
|
|
||||||
|
@ -34,5 +37,18 @@ class HttpParserTest < Test::Unit::TestCase
|
||||||
assert !parser.finished?, "Parser shouldn't be finished"
|
assert !parser.finished?, "Parser shouldn't be finished"
|
||||||
assert parser.error?, "Parser SHOULD have error"
|
assert parser.error?, "Parser SHOULD have error"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_query_parse
|
||||||
|
puts HttpRequest.query_parse("zed=1&frank=2").inspect
|
||||||
|
puts HttpRequest.query_parse("zed=1&zed=2&zed=3&frank=11;zed=45").inspect
|
||||||
|
|
||||||
|
puts Benchmark.measure {
|
||||||
|
10000.times do |i|
|
||||||
|
g = HttpRequest.query_parse("zed=1&zed=2&zed=3&frank=11").inspect
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
require 'test/unit'
|
require 'test/unit'
|
||||||
require 'mongrel'
|
require 'mongrel'
|
||||||
|
require 'benchmark'
|
||||||
|
|
||||||
include Mongrel
|
include Mongrel
|
||||||
|
|
||||||
|
@ -40,5 +41,6 @@ class ResponseTest < Test::Unit::TestCase
|
||||||
assert io.length > 0, "output didn't have data"
|
assert io.length > 0, "output didn't have data"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue