我試圖理解和沿麒麟的線重新建立一個簡單的preforking所在服務器的控制插座上開始叉子4個處理所有的等待(接受)的服務器。紅寶石readpartial和read_nonblock不扔的EOFError
的控制插座@control_socket
結合9799,併產生4名工人伺候接受連接。對每個工人完成的工作如下
def spawn_child
fork do
$STDOUT.puts "Forking child #{Process.pid}"
loop do
@client = @control_socket.accept
loop do
request = gets
if request
respond(@inner_app.call(request))
else
$STDOUT.puts("No Request")
@client.close
end
end
end
end
end
我用一個很簡單的機架應用程序,它簡單地返回與狀態碼200和文本/ HTML的內容類型的字符串。
我所面臨的問題是,我的服務器使用gets
,而不是像read
或read_partial
或的作品,因爲它應該當我讀到傳入的請求(通過點擊在「http://localhost:9799」的URL)。當我使用非阻塞讀它似乎永遠不會丟的EOFError,根據我的理解,這意味着它不會收到EOF
狀態。
這會導致讀取loop
不完整的。這是執行這項工作的代碼片段。
# Reads a file using IO.read_nonblock
# Returns end of file when using get but doesn't seem to return
# while using read_nonblock or readpartial
# The fact that the method is named gets is just bad naming, please ignore
def gets
buffer = ""
i =0
loop do
puts "loop #{i}"
i += 1
begin
buffer << @client.read_nonblock(READ_CHUNK)
puts "buffer is #{buffer}"
rescue Errno::EAGAIN => e
puts "#{e.message}"
puts "#{e.backtrace}"
IO.select([@client])
retry
rescue EOFError
$STDOUT.puts "-" * 50
puts "request data is #{buffer}"
$STDOUT.puts "-" * 50
break
end
end
puts "returning buffer"
buffer
end
然而代碼完美地工作,如果我用一個簡單gets
而不是read
或或者用break
更換IO.select([@client])
。
下面是當代碼工作並返回響應。我打算用read_nonblock的原因是麒麟使用kgio庫,實現讀取non_blocking使用等效。
def gets
@client.gets
end
整個碼被下粘貼。
require 'socket' require 'builder' require 'rack' require 'pry' module Server class Prefork # line break CRLF = "\r\n" # number of workers process to fork CONCURRENCY = 4 # size of each non_blocking read READ_CHUNK = 1024 $STDOUT = STDOUT $STDOUT.sync # creates a control socket which listens to port 9799 def initialize(port = 21) @control_socket = TCPServer.new(9799) puts "Starting server..." trap(:INT) { exit } end # Reads a file using IO.read_nonblock # Returns end of file when using get but doesn't seem to return # while using read_nonblock or readpartial def gets buffer = "" i =0 loop do puts "loop #{i}" i += 1 begin buffer << @client.read_nonblock(READ_CHUNK) puts "buffer is #{buffer}" rescue Errno::EAGAIN => e puts "#{e.message}" puts "#{e.backtrace}" IO.select([@client]) retry rescue EOFError $STDOUT.puts "-" * 50 puts "request data is #{buffer}" $STDOUT.puts "-" * 50 break end end puts "returning buffer" buffer end # responds with the data and closes the connection def respond(data) puts "request 2 Data is #{data.inspect}" status, headers, body = data puts "message is #{body}" buffer = "HTTP/1.1 #{status}\r\n" \ "Date: #{Time.now.utc}\r\n" \ "Status: #{status}\r\n" \ "Connection: close\r\n" headers.each {|key, value| buffer << "#{key}: #{value}\r\n"} @client.write(buffer << CRLF) body.each {|chunk| @client.write(chunk)} ensure $STDOUT.puts "*" * 50 $STDOUT.puts "Closing..." @client.respond_to?(:close) and @client.close end # The main method which triggers the creation of workers processes # The workers processes all wait to accept the socket on the same # control socket allowing the kernel to do the load balancing. # # Working with a dummy rack app which returns a simple text message # hence the config.ru file read. def run # copied from unicorn-4.2.1 # refer unicorn.rb and lib/unicorn/http_server.rb raw_data = File.read("config.ru") app = "::Rack::Builder.new {\n#{raw_data}\n}.to_app" @inner_app = eval(app, TOPLEVEL_BINDING) child_pids = [] CONCURRENCY.times do child_pids << spawn_child end trap(:INT) { child_pids.each do |cpid| begin Process.kill(:INT, cpid) rescue Errno::ESRCH end end exit } loop do pid = Process.wait puts "Process quit unexpectedly #{pid}" child_pids.delete(pid) child_pids << spawn_child end end # This is where the real work is done. def spawn_child fork do $STDOUT.puts "Forking child #{Process.pid}" loop do @client = @control_socket.accept loop do request = gets if request respond(@inner_app.call(request)) else $STDOUT.puts("No Request") @client.close end end end end end end end p = Server::Prefork.new(9799) p.run
有人可以向我解釋爲什麼read_partial,read_nonblock或read的讀取失敗。我非常感謝這方面的幫助。
謝謝。
您描述的行爲與文檔「EOFError」,「read_nonblock」等的含義相反。 'get'應該返回'nil','read_nonblock'應該引發'EOFError'。 –
如果您只啓動一名工人,會發生什麼情況?對於我來說,在''''''spawn_child'''方法中分配一個實例變量''@ client'''是很奇怪的。每個工人都不會重寫那個變量嗎?或者,叉是否建立它自己的上下文? – GSP