【发布时间】:2020-11-28 08:11:10
【问题描述】:
我在后台作业中使用 Rails 和 Carrierwave 将许多小文件上传到 S3,并且达到了 S3 速率限制。我的直接想法是在每次上传之前添加一个sleep 0.1,但这似乎不是一个很好的解决方案。
关于如何通过 S3 API 和某种类型的退避来处理这个问题的任何建议?
正在上传的Ruby代码,这个方法被循环调用数千次:
def attach_audio(object:, audio_field:, attachment:)
return true if Rails.env.test?
language_code, voice_id = language_and_voice(object)
resp = polly.synthesize_speech(
output_format: 'mp3',
voice_id: voice_id,
text: audio_field.to_s,
language_code: language_code
)
audio_filename = "#{object.class.to_s.downcase}_#{attachment}_#{object.id}_#{voice_id}.mp3"
audio_path = "#{Rails.root}/db/audio/#{audio_filename}"
IO.copy_stream(resp.audio_stream, audio_path)
object.send(attachment + '=', Pathname.new(audio_path).open)
object.save!
end
上传者类
class AudioUploader < BaseUploader
def store_dir
"uploads/audio/#{model.target_language}/#{self.class.to_s.underscore}/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def extension_whitelist
%w[mp3]
end
end
class BaseUploader < CarrierWave::Uploader::Base
if Rails.env.test?
storage :file
else
storage :fog
end
def store_dir
"uploads/#{self.class.to_s.underscore}/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
来自 AWS 的响应
Message
Excon::Error::ServiceUnavailable: Expected(200) <=> Actual(503 Service Unavailable) excon.error.response :body => "<Error><Code>SlowDown</Code><Message>Please reduce your request rate.</Message><RequestId>176C22715A856A29</RequestId><HostId>L/+
Traceback
Excon::Error::ServiceUnavailable: Expected(200) <=> Actual(503 Service Unavailable)
excon.error.response
:body => "<Error><Code>SlowDown</Code><Message>Please reduce your request rate.</Message><RequestId>176C22715A856A29</RequestId><HostId>xxxxxxxxxxxxxxxxxxxxxxxxx</HostId></Error>"
:cookies => [
]
:headers => {
"Connection" => "close"
"Content-Type" => "application/xml"
"Date" => "Wed, 18 Nov 2020 07:31:29 GMT"
"Server" => "AmazonS3"
"x-amz-id-2" => "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"x-amz-request-id" => "176C22715A856A29"
}
:host => "example-production.s3-eu-west-1.amazonaws.com"
:local_address => "xxx.xx.xxx.xxx"
:local_port => 50276
:path => "/uploads/audio/fr/audio_uploader/word/audio_file/8015423/word_audio_file_8015423_Mathieu.mp3"
:port => 443
:reason_phrase => "Slow Down"
:remote_ip => "xx.xxx.xx.x"
:status => 503
:status_line => "HTTP/1.1 503 Slow Down\r\n"
File "/app/vendor/bundle/ruby/2.6.0/gems/excon-0.71.1/lib/excon/middlewares/expects.rb", line 13, in response_call
File "/app/vendor/bundle/ruby/2.6.0/gems/excon-0.71.1/lib/excon/middlewares/response_parser.rb", line 12, in response_call
File "/app/vendor/bundle/ruby/2.6.0/gems/excon-0.71.1/lib/excon/connection.rb", line 448, in response
File "/app/vendor/bundle/ruby/2.6.0/gems/excon-0.71.1/lib/excon/connection.rb", line 279, in request
File "/app/vendor/bundle/ruby/2.6.0/gems/fog-xml-0.1.3/lib/fog/xml/sax_parser_connection.rb", line 35, in request
etc
编辑
链接的 AWS 文档引用了前缀,这似乎可以解决问题
Amazon S3 自动扩展到高请求率。例如,您的应用程序可以在存储桶中每个前缀每秒至少完成 3,500 个 PUT/COPY/POST/DELETE 或 5,500 个 GET/HEAD 请求。桶中的前缀数量没有限制。您可以通过并行读取来提高读取或写入性能。例如,如果您在 Amazon S3 存储桶中创建 10 个前缀来并行读取,您可以将读取性能扩展到每秒 55,000 个读取请求。
但我不明白如何在 Carrierwave 的上下文中实现它。
【问题讨论】:
-
似乎有一些前缀形式的解决方案,但是我不是很理解文章docs.aws.amazon.com/AmazonS3/latest/dev/…
-
那么您认为每秒有多少次上传?你使用 Sidekiq 还是 ActiveJob?
-
如果您阅读 aws 文档,他们建议在您开始变慢响应 docs.aws.amazon.com/general/latest/gr/api-retries.html 时使用指数退避。我对这些库不太熟悉,但我认为您可以挽救错误,解析 AWS 错误代码的主体,然后等待 (2^retry_count) 秒后再重试
-
s3fs 会是答案吗?而不是上传,您将写入一个 s3fs 挂载的分区,最终结果是相同的。你可以在这里看到我对类似问题的回答stackoverflow.com/questions/60311166/ffmpeg-pipe-segments-to-s3/…
-
就像休伯特问的那样:您有多少并行上传正在运行?我已经使用 S3 多年了,但我从来没有达到过他们的极限……