2012-11-20 9 views
12

मैं यूनिकॉर्न की लाइनों के साथ एक सरल प्रीफोरिंग सर्वर को समझने और फिर से बनाने की कोशिश कर रहा हूं, जहां प्रारंभिक सर्वर पर सर्वर 4 प्रक्रियाओं को नियंत्रित करता है जो सभी नियंत्रण सॉकेट पर प्रतीक्षा (स्वीकार करने के लिए) करते हैं।रूबी रीडपार्टियल और read_nonblock फेंक नहीं EOFError

नियंत्रण सॉकेट @control_socket 9 7 9 9 से बांधता है और 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 की सामग्री प्रकार के साथ एक स्ट्रिंग रिटर्न एक बहुत ही सरल रैक एप्लिकेशन का उपयोग किया है इस प्रकार है।

समस्या मैं का सामना

है कि मेरे सर्वर के रूप में यह होना चाहिए जब मैं आने वाले अनुरोधों को पढ़ ("http://localhost:9799" में यूआरएल को हिट करके) काम करता है की तरह read या read_partial या read_nonblock कुछ बजाय एक gets का उपयोग कर रहा है। जब मैं गैर अवरोधन का उपयोग करता हूं तो यह कभी भी 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 या read_nonblock की या यदि एक break साथ IO.select([@client]) बदलें।

यहां कोड कोड काम करता है और प्रतिक्रिया देता है। Read_nonblock का उपयोग करने का मेरा कारण यह है कि यूनिकॉर्न kgio लाइब्रेरी का उपयोग करके बराबर का उपयोग करता है जो एक गैर-अवरुद्ध पढ़ने को लागू करता है।

 

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' या 'पढ़ा' के साथ विफल मुझे समझा जा सका। मैं वास्तव में इस पर कुछ मदद की सराहना करता हूं।

धन्यवाद।

+1

आपके द्वारा वर्णित व्यवहार 'EOFError', 'read_nonblock' आदि के दस्तावेज़ों के विपरीत है। 'get'' वापस लौटना चाहिए, 'read_nonblock'' EOFError 'को बढ़ाया जाना चाहिए। –

+0

क्या होता है यदि आप केवल एक ही कार्यकर्ता शुरू करते हैं? यह मेरे लिए अजीब बात है कि आप '' '' spawn_child'''' विधि में एक आवृत्ति चर '' '' क्लाइंट '''' असाइन करते हैं। क्या प्रत्येक कर्मचारी उस चर को ओवरराइड नहीं करेगा? या, कांटा अपने स्वयं के संदर्भ स्थापित करता है? – GSP

उत्तर

9

सबसे पहले मैं कुछ बुनियादी ज्ञान के बारे में बात करना चाहता हूं, ईओएफ का मतलब फ़ाइल का अंत है, यह सिग्नल की तरह होगा जब डेटा स्रोत से कोई और डेटा नहीं पढ़ा जा सकता है, उदाहरण के लिए, एक फ़ाइल खोलें और पूरे पढ़ने के बाद फ़ाइल को एक ईओएफ प्राप्त होगा, या सिर्फ आईओ स्ट्रीम को बंद कर देगा।

तो फिर वहाँ माणिक में यह डिफ़ॉल्ट लाइन परिसीमक के रूप में $/ का उपयोग करता है, इन 4 तरीकों

  • gets स्ट्रीम से एक लाइन पढ़ता के बीच कई मतभेद हैं, लेकिन आप, लाइन परिसीमक के रूप में एक पैरामीटर पारित कर सकते हैं क्योंकि अगर क्लाइंट और सर्वर एक ही ऑपरेटिंग सिस्टम नहीं हैं, लाइन डिलीमीटर शायद अलग हो सकता है, यह ब्लॉक विधि है, यदि कभी लाइन डिलीमीटर या ईओएफ को पूरा नहीं करता है, तो यह ब्लॉक होगा, और ईओएफ प्राप्त होने पर शून्य लौटाएगा, इसलिए gets कभी मिल नहीं पाएगा एक EOFError

  • read(length) धारा से लंबाई बाइट्स पढ़ता है, यह एक ब्लॉक विधि है, अगर लंबाई तो छोड़ दिया जाता है जब तक EOF पढ़ को अवरुद्ध कर देगा, अगर वहाँ एक लंबाई है तो यह डेटा का केवल एक बार पढ़ा है निश्चित राशि देता है या EOF से मिलने , और ईओएफ प्राप्त करते समय खाली स्ट्रिंग देता है, इसलिए read कभी भी EOFError से कभी नहीं मिलेगा।

  • readpartial(maxlen) ज्यादा से ज्यादा पढ़ता maxlen बाइट्स धारा से, यह उपलब्ध डेटा को पढ़ने और तुरंत वापस आ जाएगी, यह एक तरह read के एक उत्सुक संस्करण की तरह है, अगर डेटा बहुत बड़ा आप readpartial बजाय read उपयोग कर सकते हैं को अवरुद्ध करने से रोकने के लिए है, लेकिन यह अभी भी ब्लॉक विधि है, यदि कोई डेटा तत्काल उपलब्ध नहीं है, तो readpartial यदि कोई ईओएफ प्राप्त करता है तो EOFError उठाएगा।

  • read_nonblock(maxlen) तरह readpartial तरह है, लेकिन जैसे नाम ने कहा कि यह एक nonblock विधि है, यहां तक ​​कि कोई भी डेटा उपलब्ध यह बढ़ा एक Errno::EAGAIN तुरंत यह कोई डेटा अभी इसका मतलब है, आप Errno::EAGAIN बचाव में, इस त्रुटि के बारे ध्यान देना चाहिए सामान्य रूप से खंड को कम से कम अनावश्यक चक्र के लिए IO.select([conn]) पर कॉल करना चाहिए, यह तब तक अवरुद्ध हो जाएगा जब तक कि कॉन पढ़ने के लिए उपलब्ध न हो जाए, तब retry, read_nonblock एक ईओएफ प्राप्त करने पर EOFError उठाएगा।

अब अपने उदाहरण देखते हैं, के रूप में मैं देख आप कर रहे हैं क्या है द्वारा पहले "यूआरएल मार" डेटा को पढ़ने की कोशिश करते हैं, यह सिर्फ एक HTTP GET अनुरोध, "की तरह प्राप्त/HTTP कुछ पाठ/1.1 \ है r \ n ", कनेक्शन जब तक अपने अनुरोध में Connection: close हैडर डाल/1.1 डिफ़ॉल्ट रूप से HTTP में जीवित रखने के हैं, इसलिए readpartial या read_nonblock का उपयोग कर एक EOF नहीं प्राप्त होगा, या बदलने के अपने हो जाता है के रूप में नीचे दी गई विधि:

buffer = "" 
if m = @client.gets 
    buffer << m 
    break if m.strip == "" 
else 
    break 
end 
buffer 

आप यहां read का उपयोग नहीं कर सकते हैं, क्योंकि आप अनुरोध पैकेज की सटीक लंबाई नहीं जानते हैं, बड़ी लंबाई का उपयोग करें या बस छोड़े गए ब्लॉक।

संबंधित मुद्दे