【问题标题】:How to read from an Azure Page Blob with concurrent writes?如何通过并发写入读取 Azure Page Blob?
【发布时间】:2016-04-16 09:56:26
【问题描述】:

我正在调用 Azure Storage SDK for Java (v4.0.0) 中的 downloadRange-函数来下载部分页面 Blob,例如downloadRange(0, 1000, os, null, null, null)。另一个进程(单个写入器)写入页 blob 的末尾。如果写入与downloadRange-call 并发,并且downloadRange 在内部进行重试(HTTP GET),则会产生带有以下文本的StorageException:“未满足使用 HTTP 条件标头指定的条件。”。

是否可以在不发生这种情况的情况下执行downloadRange 读取操作?就应用程序而言,访问直到最后一页的字节是安全的。

伪代码如下(使用scala):

val blob = container.getPageBlobReference(blobName)
val baos = new ByteArrayOutputStream()
blob.downloadRange(0, totalSize, baos, null, null, null)

更新

基于以下 cmets 的说明。用例有点特殊,因为已知读取的字节范围是安全的,即它只读取 blob 中未同时写入的字节范围。写入仅附加到 blob 的末尾。问题是如何使用 downloadRange 或 Azure 存储 SDK 的任何其他部分来访问具有并发写入的 blob,即使在网络问题(数据包丢失、传输缓慢等)的情况下也是如此。

【问题讨论】:

  • 除非您最终重试,否则我也不希望看到条件标头错误。我无法重现这个问题——当我发送它并在 Fiddler 中进行调查时,没有条件标题,也没有产生错误。如果您可以发布 1. 删除任何敏感信息的下载请求的跟踪(来自 Fiddler 等工具)和 2. 您正在使用的 lib 版本,那将有所帮助。
  • 另请注意:您可以只使用没有空值的 downloadRange(0, totalSize, baos)。
  • 我用wireshark查看了完整的trace。最初有一个 GET 来检索 blob。大约 41 秒后,服务器端关闭 TCP 连接(RST ACK)。当客户端使用新的 GET 重试时,它包含一个 If-Match 标头,其中包含初始 GET 响应的 ETag 部分。这会触发错误,因为在下载期间发生了写入。是否可以避免在重试时发送 If-Match 标头?
  • 是的,重试就是这里的问题。 (你能用这个细节更新你的问题吗?)。设置 if-match 是因为在重试时我们必须保证 blob 没有更改以保持一致性。否则,如果在这些调用之间设置了一个新的 blob,例如,我们将得到一半旧的和一半的新 blob。根本问题似乎是 TCP 重置。 blob 有多大,网络是否特别慢?
  • blob 约为 32MB,从该特定存储帐户下载速度很慢 (0.5-1MBps)。从同一 DC 中的其他存储帐户,我在同一连接上获得 3-4MBps。关于根本问题,恕我直言,问题在于downloadRange 的语义。在一致性方面,第一个 GET 是“尽力而为”读取,即写入者可以更改 blob 中的任何页面(在当前读取偏移之前或之后)。第二个 GET 是一个乐观读取,只有在第一个 GET 之后没有任何变化的情况下才会成功。因此,它与使用 If-Match AccessCondition 调用 downloadRange 相同。

标签: java azure azure-storage azure-blob-storage


【解决方案1】:

StorageException 的问题似乎是因为没有为 Class CloudBlob 的函数 downloadRange 设置参数 AccessCondition

关于Azure Storage并发,我推荐官方文档Managing Concurrency in Microsoft Azure Storage。 C# 中有一些代码示例作为参考。您可以尝试参考the Javadocs of Azure Storage SDK 并将这些代码翻译成Java。

如有任何疑问,请随时告诉我。

【讨论】:

  • 我尝试将 ifNoneMatch("*") 添加到请求中,但仍然抛出异常。查看 GET Blob 的 REST API 文档msdn.microsoft.com/en-us/library/azure/dd179440.aspx,我认为它可以在没有条件 HTTP 标头的情况下成功。
  • @mkhq 请参阅msdn.microsoft.com/en-us/library/azure/dd179371.aspxSupported Conditional Headers 部分,它说“指定通配符(*)仅在资源不存在时执行操作,并且失败如果确实存在,则进行操作。” 您能否分享您的代码以解决此问题?
  • 我已经用一些基本代码更新了这个问题。我没有使用任何条件标头,并希望当 AccessCondition 为 nulldownloadRange 不会添加任何内容。
【解决方案2】:

此答案基于您可以在上面阅读的评论线程。

在这种特殊情况下,错误发生在重试而不是第一次调用时。当存储库重试下载时,if-match 被设置,因为在重试时,我们必须保证 blob 没有更改以保持一致性。否则,如果在这些调用之间设置了新的 blob,例如,我们将得到一半旧的和一半的新 blob。从库的角度来看,由于我们不知道如果 blob 发生更改,后续读取将是安全的,我们必须强制执行此操作。没有办法禁用它。

在这种特殊情况下,频繁的连接失败、并发写入和对读取安全的预知是一个非常独特的组合。这是我预计通常非常罕见的事情。连接失败将是最罕见的部分,因此可能需要更多调查(也许是另一个问题的主题)。

在这种特殊情况下,我建议尽可能减少网络打开的时间。减少此时间意味着首先遇到更少的网络故障会降低如果确实发生重试,则 blob 已更改的可能性,因为小型下载将花费更少的时间。将你的读取分解成更小的块可能是实现这一目标的最佳途径。同样,您可能希望在 catch 语句中手动重试这小部分下载以解决此错误。

【讨论】:

    猜你喜欢
    • 2023-02-01
    • 2021-11-15
    • 2018-01-08
    • 1970-01-01
    • 2012-04-25
    • 2017-04-03
    • 1970-01-01
    • 2021-10-13
    • 1970-01-01
    相关资源
    最近更新 更多