Heroku, 나는 당신을 스트리밍합니다!
페이지에서 CSV, XLSX, PDF 등과 같은 형식으로 내보내기 기능을 갖는 것은 매우 일반적인 기능입니다.
이것은 #respond_to 내부의 Rails에서 간단한 것입니다.
def index
@records = ...
respond_to do |format|
format.html # index.html
format.csv { some_csv_generation_code }
end
end
그러나 언젠가 사용자가 많은 정보를 다운로드하려고 하면 오류가 나타나기 시작합니다.
서버는 특정 시간이 지나면 시간 초과 오류로 응답하고 파일을 생성하는 요청을 중지합니다.
Heroku와 같은 PaaS에서 이 시간 제한은 생성된 콘텐츠가 없는 30초입니다. 또한 각각의 새로운 출력 조각 사이에는 55''의 또 다른 타임아웃이 있습니다. 공식 정보 인용:
An application has an initial 30 second window to respond with a single byte back to the client. However, each byte transmitted thereafter (either received from the client or sent by your application) resets a rolling 55 second window. If no data is sent during the 55 second window, the connection will be terminated.
Rails에는 ActionController::Live에서 이러한 종류의 출력 생성을 처리하는 스트리밍 기능이 있는 것 같습니다. 또한 Rails edge(향후 v7)에는 이 기능을 달성하는 것으로 보이는 #send_stream 메서드가 있으므로 컨트롤러 문제로 내 Rails 5.2 앱에 복사하고 Enumerable #each 메서드를 구현하도록 CSV 생성 논리를 변경했습니다.
Last-Modified 헤더도 재정의하기 전까지는 작동하지 않았습니다. this issue discussion을 참조하십시오.
이것은 컨트롤러 문제의 전체 코드입니다.
require 'content_disposition'
module Streamable
extend ActiveSupport::Concern
include ActionController::Live
# Almost verbatim copy of new Rails 7 method.
# See https://edgeapi.rubyonrails.org/classes/ActionController/Live.html#method-i-send_stream
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'] = ContentDisposition.format(disposition: disposition, filename: filename)
# with rails 6 remove content_disposition gem and use:
# ActionDispatch::Http::ContentDisposition.format(disposition: disposition, filename: filename)
# Extra: needed for streaming
response.headers['Last-Modified'] = Time.now.httpdate
yield response.stream
ensure
response.stream.close
end
end
Rails 5.x의 경우 Content-Disposition 헤더를 설정하기 위해 추가 gem이 필요했습니다.
gem 'content_disposition', '~> 1.0' # Not needed on Rails 6
느린 더미 데이터를 생성하는 컨트롤러의 테스트 예:
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 = "pepe_#{i}@acme.com,#{Time.zone.now}\n"
stream.write line
puts line
sleep 1 # force slow response
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
이제 앱에서 챔피언처럼 대용량 데이터를 스트리밍할 수 있습니다!.
이 앱을 Heroku app에 배포했고 코드는 this github repo에 있습니다.
추가 성능 팁. ActiveRecord를 사용하여 DB에서 수많은 레코드를 가져오는 경우 #each 대신 #find_each를 사용하여 일괄적으로 레코드를 가져올 수 있습니다.
이것이 도움이 되었는지 알려주세요.
즐거운 Rails 해킹!
Reference
이 문제에 관하여(Heroku, 나는 당신을 스트리밍합니다!), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/megatux/heroku-i-stream-you-4mdb텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)