【问题标题】:NoMemoryError when downloading Azure Blob in Ruby在 Ruby 中下载 Azure Blob 时出现 NoMemoryError
【发布时间】:2017-09-29 04:10:05
【问题描述】:

环境:

  • Windows 10 x64
  • Ruby 2.1.0 32 位
  • 厨师 12.12.15
  • 蔚蓝宝石 0.7.9
  • Azure-Storage Gem 0.12.1.preview

我正在尝试从容器中下载一个 ~880MB 的 blob。当我这样做时,它会在 Ruby 进程达到约 500MB 大小后引发以下错误:

C:/opscode/chefdk/embedded/lib/ruby/2.1.0/net/protocol.rb:102:in `read': 分配内存失败 (NoMemoryError)

我已经在 Ruby 内外以及 Azure gem 和 Azure-Storage gem 中尝试过。结果对于所有四种组合(Chef 中的 Azure、Ruby 中的 Azure、Chef 中的 Azure-Storage、Ruby 中的 Azure-Storage)都是相同的。

我发现的针对此类问题的大多数故障排除建议使用流式传输或分块下载,但似乎没有相应的方法或 get_blob 选项来执行此操作。

代码:

require 'azure/storage'

# vars
account_name = "myacct"
container_name = "myfiles"
access_key = "mykey"
installs_dir = "myinstalls"

# directory for files
create_dir = 'c:/' + installs_dir
Dir.mkdir(create_dir) unless File.exists?(create_dir)

# create azure client
Azure::Storage.setup(:storage_account_name => account_name, :storage_access_key => access_key)
azBlobs = Azure::Storage::Blob::BlobService.new

# get list of blobs in container
dlBlobs = azBlobs.list_blobs(container_name)

# download each blob to directory
dlBlobs.each do |dlBlob|
    puts "Downloading " + container_name + "/" + dlBlob.name
    portalBlob, blobContent = azBlobs.get_blob(container_name, dlBlob.name)
    File.open("c:/" + installs_dir + "/" + portalBlob.name, "wb") {|f|

        f.write(blobContent)
    }
end

我也尝试使用 IO.binwrite() 而不是 File.open() 并得到相同的结果。

建议?

【问题讨论】:

    标签: ruby azure chef-infra azure-storage azure-blob-storage


    【解决方案1】:

    我只是在考虑将azure/storage/blob 库用于我正在从事的开发运维项目,在我看来,该实现是非常基本的,并且没有利用可用的完整底层 API。例如,从文件流式传输时上传速度很慢,因为很可能它没有并行上传块等。我不认为这个库已经准备好生产并且缺少暴露的 ruby​​ API。它是开源的,所以如果有人有时间,他们可以提供帮助。

    【讨论】:

      【解决方案2】:

      正如@coderanger 所说,您的问题是由使用get_blob 将本地数据一次放入内存引起的。有两种解决方法。

      1. 根据官方 REST 参考 here 如下。

      在 2016-05-31 及更高版本中,通过 Put Blob 创建的块 Blob 的最大大小为 256 MB,旧版本为 64 MB。如果您的 blob 对于版本 2016-05-31 及更高版本大于 256 MB,或者对于旧版本大于 64 MB,则必须将其作为一组块上传。有关详细信息,请参阅 Put Block 和 Put Block List 操作。如果您将 Blob 作为一组块上传,则无需调用 Put Blob。

      所以对于一个由block blob组成的blob,你可以尝试通过list_blob_blocks获取block blob列表,将这些block blob一一写入本地文件。

      1. 通过signed_uri (如this test code)生成带有SAS 令牌的blob url,然后通过流式传输下载blob 以写入本地文件。

      【讨论】:

      • 感谢测试代码!我一直在尝试自己解决这个问题,这很简单。肯定比消耗块更容易:-)
      【解决方案3】:

      问题在于get_blob 必须立即将数据加载到内存中,而不是将其流式传输到磁盘。在 Chef 中,我们有 remote_file 资源来帮助进行流式下载,但您需要获取 blob 的纯 URL,而不是使用他们的 gem 下载它。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-01-16
        • 2019-03-19
        • 2017-03-02
        • 2020-10-14
        • 2013-04-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多