如何通过并发写入从 Azure Page Blob 中读取?

How to read from an Azure Page Blob with concurrent writes?

我正在从 Java (v4.0.0) 的 Azure 存储 SDK 中调用 downloadRange 函数来下载部分页面 Blob,例如downloadRange(0, 1000, os, null, null, null)。另一个进程(单个写入器)写入页面 blob 的末尾。如果写入与 downloadRange 调用并发并且 downloadRange 在内部重试 (HTTP GET ),则会导致 StorageException 包含以下文本:"The condition specified using HTTP conditional header(s) is not met.".

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

伪代码如下(使用scala):

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

更新

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

问题StorageException似乎是由于没有为ClassCloudBlob的函数downloadRange设置参数AccessCondition引起的CloudBlob

关于Azure Storage并发,推荐官方文档Managing Concurrency in Microsoft Azure Storage. There are some code samples in C# as references. You can try to refer to the Javadocs of Azure Storage SDK,翻译这些代码在Java.

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

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

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

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

在这种特殊情况下,我建议尽一切可能减少网络打开的时间。减少这个时间意味着首先遇到较少的网络故障会降低 blob 在重试发生时发生变化的可能性,因为小的下载只会花费更少的时间。将你的阅读分解成更小的块可能是实现这一目标的最佳途径。同样,对于此错误,您可能只想在 catch 语句中手动重试这一小部分下载。