Как стримить ответные рельсы приложения на героку

У меня есть приложение rails 3.1, работающее на герою. Мне нужно предоставить пользователю возможность загружать данные CSV. Я пытаюсь передать данные, но все они отправляются за один раз. Который для больших запросов будет тайм-аут.

На сайте heroku много говорят о потоковой передаче и порции, но, насколько я могу судить, thin собирает все данные и отправляет их за один раз. Как мне заставить его работать?

Должен ли я добавить промежуточное программное обеспечение? например, единорог. Потоки кода нормально работают с монгрелом.

2 ответа

Я уверен, что вам просто нужно добавить

stream

на вершину вашего контроллера.

Более подробную информацию о потоковой передаче HTTP можно найти на RailsCasts: http://railscasts.com/episodes/266-http-streaming

Этот вопрос действительно старый, но проблема все еще очень распространена из-за 30-дюймового ограничения в ответах Heroku, поэтому я добавлю немного кода о том, как я этого добился. Работает с Rails 5.2 и 6.1 на Heroku с сервером Puma.

Я использую метод #send_stream (присутствует только в краевых направляющих, в будущих направляющих 7), поэтому я просто скопировал его + вручную установил заголовок Last-Modified. Добавил все в рельсы для повторного использования.

      module Streameable
  extend ActiveSupport::Concern
  include ActionController::Live

  def send_stream(filename:, disposition: 'attachment', type: nil)
    response.headers['Content-Type'] =
      (type.is_a?(Symbol) ? Mime[type].to_s : type) ||
      Mime::Type.lookup_by_extension(File.extname(filename).downcase.delete('.')) ||
      'application/octet-stream'

    response.headers['Content-Disposition'] =
      ActionDispatch::Http::ContentDisposition.format(disposition: disposition, filename: filename)  # for Rails 5, use content_disposition gem

    # extra: needed for streaming correctly
    response.headers['Last-Modified'] = Time.now.httpdate

    yield response.stream
  ensure
    response.stream.close
  end
end

class ExporterController < ApplicationController
  include Streameable

  def index
    respond_to do |format|
      format.html # index.html
      format.js   # index.js
      format.csv do
        send_stream(attachment_opts) do |stream|
          stream.write "email_address,updated_at\n"

          50.times.each do |i|
            line = "user_#{i}@acme.com,#{Time.zone.now}\n"
            stream.write line
            puts line
            sleep 1  # force slow response for testing respose > 30''
          end
        end
      end
    end
  end

  private

  def attachment_opts
    {
      filename: "data_#{Time.zone.now.to_i}.csv",
      disposition: 'attachment',
      type: 'text/csv'
    }
  end
end

Затем, если вы используете что-то вроде curl, вы будете видеть результат, генерируемый каждую секунду.

      $ curl -i  http://localhost:3000/exporter.csv 

Важно написать код для перебора данных с помощью #each, используя модуль Enumerable. О, совет с ActiveRecord, используйте # чтобы find_each,выборка из БД производилась партиями.

Другие вопросы по тегам