AWS::S3 put_object 使用 aws-sdk-ruby 非常慢

问题描述 投票:0回答:5

我们在 Heroku 托管的 Rails 应用程序上的后台工作程序上生成 PDF 文件,一旦生成,它们就会上传到 Amazon S3。 Heroku 应用程序和 S3 存储桶均位于 eu-west-1 区域。

我们的上传速度非常慢,尽管文件非常基本且很小。看这个例子:

Aws.config.update({
  region: 'eu-west-1',
  credentials: Aws::Credentials.new(ENV['S3_USER_KEY'], ENV['S3_USER_SECRET'])
})

S3_BUCKET = Aws::S3::Resource.new.bucket(ENV['S3_PRIVATE_BUCKET'])

file = Tempfile.new(["testfile", ".pdf"], encoding: "ascii-8bit").tap do |file|
  file.write("a"*5000)
end

Benchmark.bm do |x|
  x.report { S3_BUCKET.put_object(key: "testfile.pdf", body: file) }
end

   user       system     total      real
   0.020000   0.040000   0.060000   ( 40.499553)

我想我无法举一个更简单的例子,因此发送一个包含 5000 个字符的文件需要 40 秒才能从 Heroku 一次性实例上传到 S3。

请注意,我在我的(国内)互联网连接和 Heroku 实例上进行了测试,结果几乎相似。 另一方面,我使用 ForkLift.app 作为 GUI 来浏览我的存储桶,并且上传文件几乎是瞬时的。

我一直在浏览 aws-sdk 文档,但看不到任何内容来解释如此缓慢的上传

ruby-on-rails amazon-s3 aws-sdk
5个回答
1
投票

看来是put_object和TempFile的问题

先尝试将文件传递给IO

new_file = IO.read(file)
S3_BUCKET.put_object(key: "testfile.pdf", body: new_file)

0
投票

看起来 AwsSdk 是罪魁祸首。 我测试了其他方式上传相同的文件:

使用 AWS CLI

(我使用手机连接,因此网络速度非常慢,而且我没有花时间在 Heroku Dyno 上安装/配置 aws CLI)

Benchmark.bm do |x|
  x.report { `aws s3 cp #{file.path} s3://#{ ENV['S3_BUCKET']}/testfile.pdf` }
end

0.000000   0.000000   0.510000 (  2.486112)

使用雾AWS

这是从 Heroku Dyno 运行的。

connection = Fog::Storage.new({
  :provider                 => 'AWS',
  :aws_access_key_id        => ENV['S3_USER_KEY'],
  :aws_secret_access_key    => ENV['S3_USER_SECRET'],
  region: "eu-west-1"
})

directory = connection.directories.new(key: ENV["S3_BUCKET"], region: "eu-west-1")

Benchmark.bmb do |x|
  x.report do
    directory.files.create(
      :key    => 'test-with-fog.pdf',
      :body   => file,
    )
  end
end

       user     system      total        real
   0.010000   0.010000   0.020000 (  0.050712)

我将坚持使用最新版本作为解决方法。不过,我没有找到导致 aws-sdk 如此缓慢的原因。


0
投票

我在创建

Aws::S3::Object
并使用方法
upload_file
时遇到了类似的问题。如果我传入一个
TempFile
对象,上传一个小文件(~5KB)也需要~40 秒。然而,传入
TempFile.path
的速度快得惊人(不到1秒)。

出于自己的原因,您可能需要使用

AWS::S3::Bucket
方法
put_object
,但是
put_object
似乎只接受
String
IO
,而不是
File
TempFile
路径。如果您可以重构以创建
AWS::S3::Object
并使用
upload_file
,您可以使用此解决方法。

require 'aws-sdk-s3'

s3_resource = Aws::S3::Resource.new(region: 'us-east-2')

file = Tempfile.new(["testfile", ".pdf"], encoding: "ascii-8bit").tap do |file|
  file.write("a"*5000)
end

Benchmark.bm do |x|
  x.report {
    obj = s3_resource.bucket('mybucket').object("testfile-as-object.pdf")

    #passing the TempFile object is quite slow
    obj.upload_file(file)
  }
end

#       user     system      total        real
#   0.010359   0.006335   0.016694 ( 41.175544)

Benchmark.bm do |x|
  x.report {
    obj = s3_resource.bucket('mybucket').object("testfile-as-path.pdf")

    #passing the TempFile object's path is massively faster than passing the TempFile object itself
    obj.upload_file(file.path)
  }
end

#       user     system      total        real
#   0.004573   0.002032   0.006605 (  0.398605)


0
投票

我认为你在 put_object 中缺少 content_type。

S3_BUCKET.put_object(key: "testfile.pdf", body: 文件, content_type: "application/pdf")

我与此具有相同的测试性能,对于 pdf 文件上传,它会从 20 秒增加到 2 毫秒。

希望这有帮助。


0
投票

我的解决方案:

Benchmark.bm do |x|
  file.seek(0) # add this
  x.report { S3_BUCKET.put_object(key: "testfile.pdf", body: file) }
end

# user       system     total      real
# 0.003022   0.000740   0.003762   (0.018425)
© www.soinside.com 2019 - 2024. All rights reserved.