ruby on rails - रेल पर रूबी 3: क्लाइंट के माध्यम से रेल के माध्यम से डेटा स्ट्रीमिंग




ruby-on-rails streaming (7)

Exequiel के सुझाव के साथ जॉन के समाधान को लागू करने के लिए मेरे लिए काम किया।

बयान

self.response.headers['Last-Modified'] = Time.now.to_s

रैक में गैर-कैशबल के रूप में प्रतिक्रिया को चिह्नित करता है।

आगे की जांच के बाद, मुझे लगा कि कोई इसका भी उपयोग कर सकता है:

headers['Cache-Control'] = 'no-cache'

यह मेरे लिए थोड़ा सा सहज है। यह संदेश किसी भी अन्य को संदेश देता है जो मेरा कोड पढ़ सकता है। इसके अलावा, यदि रैक का भविष्य संस्करण अंतिम-संशोधित की जांच बंद कर देता है, तो बहुत से कोड तोड़ सकते हैं और लोगों के लिए यह पता लगाना कुछ समय हो सकता है कि क्यों।

मैं रेल ऐप पर रूबी पर काम कर रहा हूं जो रैकस्पेस क्लाउडफाइल के साथ संचार करता है (अमेज़ॅन एस 3 के समान लेकिन कुछ सुविधाओं की कमी)।

प्रति-ऑब्जेक्ट एक्सेस अनुमतियों और क्वेरी स्ट्रिंग प्रमाणीकरण की उपलब्धता की कमी के कारण, उपयोगकर्ताओं को डाउनलोड को किसी एप्लिकेशन के माध्यम से मध्यस्थ होना पड़ता है।

रेल 2.3 में, ऐसा लगता है कि आप निम्नानुसार प्रतिक्रियाशील रूप से प्रतिक्रिया बना सकते हैं:

# Streams about 180 MB of generated data to the browser.
render :text => proc { |response, output|
  10_000_000.times do |i|
    output.write("This is line #{i}\n")
  end
}

( http://api.rubyonrails.org/classes/ActionController/Base.html#M000464 )

10_000_000.times... बजाय 10_000_000.times... मैं वहां अपने क्लाउडफाइल स्ट्रीम जनरेशन कोड को डंप कर सकता था।

परेशानी यह है कि जब मैं रेल 3 में इस तकनीक का उपयोग करने का प्रयास करता हूं तो यह वह आउटपुट होता है।

#<Proc:[email protected]/Users/jderiksen/lt/lt-uber/site/app/controllers/prospect_uploads_controller.rb:75>

ऐसा लगता है कि proc ऑब्जेक्ट की call विधि को नहीं कहा जा रहा है? कोई अन्य विचार?


इसके अतिरिक्त, आपको अपने द्वारा 'सामग्री-लंबाई' शीर्षलेख सेट करना होगा।

यदि नहीं, तो लंबाई निर्धारित करने के लिए रैक को प्रतीक्षा करना होगा (स्मृति में शरीर डेटा को बफर करना)। और यह ऊपर वर्णित विधियों का उपयोग करके अपने प्रयासों को बर्बाद कर देगा।

मेरे मामले में, मैं लंबाई निर्धारित कर सकता था। ऐसे मामलों में आप नहीं कर सकते हैं, आपको रैक को 'सामग्री-लंबाई' शीर्षलेख के बिना शरीर भेजने शुरू करने की आवश्यकता है। 'रन' से पहले 'आवश्यकता' के बाद config.ru "रैक :: चंकड" का उपयोग करें। (धन्यवाद arkadiy)


उपर्युक्त सभी पदों के लिए धन्यवाद, यहां बड़े सीएसवी स्ट्रीम करने के लिए पूरी तरह से काम करने वाला कोड है। यह कोड:

  1. किसी भी अतिरिक्त रत्न की आवश्यकता नहीं है।
  2. Model.find_each () का उपयोग करता है ताकि सभी मिलान करने वाली वस्तुओं के साथ स्मृति को ब्लूट न किया जा सके।
  3. रेलवे 3.2.5, रूबी 1.9.3 और यूनिकॉर्न का उपयोग एकल डिनो के साथ किया गया है।
  4. हर 500 पंक्तियों में एक जीसी.स्टार्ट जोड़ता है, ताकि उसके अनुयायी डिनो की अनुमति स्मृति को उड़ाना न पड़े।
  5. आपको अपने मॉडल की मेमोरी पदचिह्न के आधार पर GC.start समायोजित करने की आवश्यकता हो सकती है। मैंने बिना किसी समस्या के 9.7 एमबी के सीएसवी में 105 के मॉडल को स्ट्रीम करने के लिए इसका सफलतापूर्वक उपयोग किया है।

नियंत्रक विधि:

def csv_export
  respond_to do |format|
    format.csv {
      @filename = "responses-#{Date.today.to_s(:db)}.csv"
      self.response.headers["Content-Type"] ||= 'text/csv'
      self.response.headers["Content-Disposition"] = "attachment; filename=#{@filename}"
      self.response.headers['Last-Modified'] = Time.now.ctime.to_s

      self.response_body = Enumerator.new do |y|
        i = 0
        Model.find_each do |m|
          if i == 0
            y << Model.csv_header.to_csv
          end
          y << sr.csv_array.to_csv
          i = i+1
          GC.start if i%500==0
        end
      end
    }
  end
end

config / unicorn.rb

# Set to 3 instead of 4 as per http://michaelvanrooijen.com/articles/2011/06/01-more-concurrency-on-a-single-heroku-dyno-with-the-new-celadon-cedar-stack/
worker_processes 3

# Change timeout to 120s to allow downloading of large streamed CSVs on slow networks
timeout 120

#Enable streaming
port = ENV["PORT"].to_i
listen port, :tcp_nopush => false

Model.rb

  def self.csv_header
    ["ID", "Route", "username"]
  end

  def csv_array
    [id, route, username]
  end

ऐसा लगता है कि यह रेल 3 में उपलब्ध नहीं है

https://rails.lighthouseapp.com/projects/8994/tickets/2546-render-text-proc

यह मेरे नियंत्रक में मेरे लिए काम करने के लिए दिखाई दिया:

self.response_body =  proc{ |response, output|
  output.write "Hello world"
}

मैंने लाइटहाउस टिकट में टिप्पणी की, बस खुद को कहना चाहता था self.response_body = proc दृष्टिकोण मेरे लिए काम करता था हालांकि मुझे वेबबैक के बजाय मोंगल का उपयोग करने की आवश्यकता थी।

मार्टिन


यदि आप प्रतिक्रिया को किसी ऐसे ऑब्जेक्ट को असाइन कर रहे हैं जो # एच विधि का जवाब देता है और प्रतिक्रिया बंद होने तक यह बफरिंग होती है, तो कार्रवाई नियंत्रक में आज़माएं:

self.response.headers ['अंतिम-संशोधित'] = Time.now.to_s


#each को उस ऑब्जेक्ट को असाइन करें जो #each जवाब देता है:

class Streamer
  def each
    10_000_000.times do |i|
      yield "This is line #{i}\n"
    end
  end
end

self.response_body = Streamer.new

यदि आप 1.9.एक्स या Backports मणि का उपयोग कर रहे हैं, तो आप Enumerator.new का उपयोग करके इसे और अधिक कॉम्पैक्टली से लिख सकते हैं:

self.response_body = Enumerator.new do |y|
  10_000_000.times do |i|
    y << "This is line #{i}\n"
  end
end

ध्यान दें कि जब डेटा फ़्लश किया जाता है तो रैक हैंडलर और अंतर्निहित सर्वर पर निर्भर करता है। मैंने पुष्टि की है कि उदाहरण के लिए, Mongrel डेटा स्ट्रीम करेगा, लेकिन अन्य उपयोगकर्ताओं ने खबर दी है कि उदाहरण के लिए, वेबब्रिक इसे तब तक बफर करता है जब तक कि प्रतिक्रिया बंद न हो जाए। फ्लश करने के लिए प्रतिक्रिया को मजबूर करने का कोई तरीका नहीं है।

रेल 3.0.x में, कई अतिरिक्त गॉथस हैं:

  • विकास मोड में, गणना के भीतर से मॉडल कक्षाओं तक पहुंचने जैसी चीजें करना क्लास रीलोडिंग के साथ खराब बातचीत के कारण समस्याग्रस्त हो सकता है। रेलवे 3.0.x में यह एक खुली बग है
  • रैक और रेल के बीच बातचीत में एक बग प्रत्येक अनुरोध के लिए #each को दो बार बुलाया जाता है। यह एक और खुली बग है । आप निम्नलिखित बंदर पैच के साथ इसके आसपास काम कर सकते हैं:

    class Rack::Response
      def close
        @body.close if @body.respond_to?(:close)
      end
    end
    

दोनों समस्याएं रेल 3.1 में तय की गई हैं, जहां HTTP स्ट्रीमिंग एक मार्की सुविधा है।

ध्यान दें कि अन्य आम सुझाव, self.response_body = proc {|response, output| ...} self.response_body = proc {|response, output| ...} , रेल 3.0.x में काम करता है, लेकिन 3.1 में इसे हटा दिया गया है (और वास्तव में डेटा स्ट्रीम नहीं करेगा)। किसी ऑब्जेक्ट को असाइन करना जो सभी रेल 3 संस्करणों में # प्रत्येक कार्य का जवाब देता है।





cloudfiles