Multiple threads on single port


#1

Hi all

Can I use Ruby threads to have more then 1 server process running on the
same port

something like

require ‘socket’
port = (ARGV[0] || 80).to_i
server = TCPServer.new(‘localhost’, port)
threads = []

10.times do |i|
threads[i] = Thread.new {
while (session = server.accept)
puts “Request: #{session.gets}”
session.print “HTTP/1.1 200/OK\r\nContent-type: text/html\r\n\r\n”
session.print “

#{Time.now}

\r\n”
session.close
end
}
end

Would this produce 10 processes that could take independent connection?


#2

Stuart B. wrote:

Hi all

Can I use Ruby threads to have more then 1 server process running on the
same port

something like

[… example elided …]

Not quite. I believe you can only have one thread listening to a port
at a given time. The idiom generally is to accept the connection in one
thread, and then pass the accepted connection to a different thread for
processing. So one thread handles accepting and multiple threads manage
the processing.

– Jim W.


#3

On 6/6/06, Stuart B. removed_email_address@domain.invalid wrote:

threads = []
}
end

Would this produce 10 processes that could take independent connection?

This will probably create multiple threads that accept connections. I
only did
while(session = server.accept)
Thread.new{…}
end
and it was in C. But you should be aware of the fact that depending on
the request processing you do in your thread you may not get any
thread switch until the request is handled. But at least print should
probably trigger the ruby thread scheduler.

HTH

Michal


#4

It works, but not terribly well. Whenever you call TCPServer#accept in a
program with more than one thread, the acceptor socket goes on a wait
queue
inside Ruby until it’s readable- it doesn’t cross into the kernel. But I
tried your program and stress-tested it with ten threads, and
performance
was abysmal. So I’d guess there’s some interaction with Ruby’s thread
scheduler that isn’t quite normal. I’m not sure what you’re trying to do
but
you might be better off calling accept in one thread and passing the
accepted sockets to a thread pool, or using nonblocking IO. Another
standard
alternative is to fork a process after accepting each socket- this is a
perfectly good solution if your performance needs are low.


#5

On Tuesday 06 June 2006 8:55 am, Stuart B. wrote:

session.print “HTTP/1.1 200/OK\r\nContent-type: text/html\r\n\r\n”
session.print “

#{Time.now}

\r\n”
session.close
end
}
end

Don’t do this.

Just have a thread of execution that is in a while loop doing the
accepts on
the socket, and have it spawn threads to handle the connections.

def handle_connection(session)
puts “Request: #{session.gets}”
session.print “HTTP/1.1 200/OK\r\nContent-type: text/html\r\n\r\n”
session.print “

#{Time.now}

\r\n”
session.close
end

while (session = server.accept)
Thread.new {handle_connection(session)}
end

Kirk H.


#6

Ok, the final code for this example I have done is

require ‘socket’
port = (ARGV[0] || 123).to_i
server = TCPServer.new(‘localhost’, port)
threads = []

10.times do |i|
threads[i] = Thread.new {
while (session = server.accept)
puts “Request: #{session.gets}”
session.print “HTTP/1.1 200/OK\r\nContent-type:
text/html\r\n\r\n”
session.print

#{Time.now}

\r\n”
session.close
end
}
end

threads.each { |t| t.join}

However, the only stress I can do is the ab apache test, this shows the
same 1, 10 or 100 threads, how do I do a proper stress test??

Many thanks

Stuart