超过 4mb 的 AppendBlockAsync 在本地工作,但无法在 Azure 应用服务上工作

Ale*_*lex 4 .net-core azure-blob-storage azure-storage-account

我正在尝试重现使用 AppendBlobs 时在生产服务器上看到的问题。

文档指出

追加 blob 中的每个块可以具有不同的大小,最大可达 4 MiB,并且追加 blob 最多可包含 50,000 个块。因此,附加 blob 的最大大小略大于 195 GiB(4 MiB X 50,000 块)。

这与我在生产应用程序中看到的情况相符,而且我确实看到了这些异常:

请求正文太大,超出了最大允许限制。
RequestId:3cb3ffd7-001e-0087-5789-ae3e0c000000
时间:2023-07-04T15:10:01.2687679Z
状态:413(请求正文太大,超出了最大允许限制。) ErrorCode:RequestBodyTooLarge

我遇到的问题是我无法在测试中重现此问题。

下面是一个最小的可重现示例,它本质上是通过将一堆 GUID 序列化为字符串来创建指定大小的内存流。

然后我用来AppendBlob附加斑点......

我可以看到memoryStream.Length确实大于4mb。

然而,令人费解的是,这确实有效。文件已正确上传到 Blob 存储,没有任何异常。

我已经找到了“修复”异常的方法(例如,对内存流进行分块),但我试图首先在测试中重现此问题,但我似乎无法在任何地方重现该错误。

有什么想法吗?

[Fact]
public async Task Can_append_blob_even_if_larger_than_4mb()
{
    var containerClient  = new BlobServiceClient(ConnectionString)
        .GetBlobContainerClient("test-123");
    
    await containerClient.CreateIfNotExistsAsync();

    var outputFilename = $"Test-{DateTime.UtcNow.Ticks}.txt";
    var appendBlobClient = containerClient.GetAppendBlobClient(outputFilename);
    await appendBlobClient.CreateIfNotExistsAsync();

    var json = JsonConvert
        .SerializeObject(CreateList(6));

    var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(json));

    await appendBlobClient
        .AppendBlockAsync(memoryStream);
}

private static List<object> CreateList(int sizeInMb)
{
    const int mbInBytes = 1024 * 1024;

    var maxSizeInBytes = sizeInMb * mbInBytes;

    var totalSize = 0;

    var list = new List<object>();

    while (totalSize < maxSizeInBytes)
    {
        var obj = Guid.NewGuid();
        var serializedObj = JsonConvert.SerializeObject(obj);
        
        var objectSize = Encoding.UTF8.GetBytes(serializedObj).Length;

        if (objectSize + totalSize > maxSizeInBytes)
        {
            break;
        }

        list.Add(obj);
        totalSize += objectSize;
    }

    return list;
}
Run Code Online (Sandbox Code Playgroud)

Ant*_*ony 5

看看源码int AppendBlobClient.AppendBlobMaxAppendBlockBytes

这是

public virtual int AppendBlobMaxAppendBlockBytes => 
    ClientConfiguration.Version < BlobClientOptions.ServiceVersion.V2022_11_02
        ? Constants.Blob.Append.Pre_2022_11_02_MaxAppendBlockBytes
        : Constants.Blob.Append.MaxAppendBlockBytes;
Run Code Online (Sandbox Code Playgroud)

这些常数是:

 public const int Pre_2022_11_02_MaxAppendBlockBytes = 4 * Constants.MB; // 4MB
 public const int MaxAppendBlockBytes = 100 * Constants.MB; // 100MB
Run Code Online (Sandbox Code Playgroud)

这个更大的尺寸尚未被记录。Azure.Storage.Blobs Version 12.17.0这是在2023-07-11 发布的包中定义的。

然而,在之前的包版本中,12.16.0我们看到了一些不同的东西:

public virtual int AppendBlobMaxAppendBlockBytes => Constants.Blob.Append.MaxAppendBlockBytes;
        
const int MaxAppendBlockBytes = 4 * Constants.MB; // 4MB
Run Code Online (Sandbox Code Playgroud)

假设:

测试代码和容器使用新的、更大的 100Mb 值。失败的代码使用较小的 4Mb 值。

看起来此检查是由Azure执行的,而不是由 Azure 客户端代码执行的,因此更新 Azure 客户端包本身并不能解决问题;事实上,如果它告诉您可以写入 100Mb,而您却不能,则情况可能会更糟,反之亦然。

这可以解释为什么最近创建的容器可以工作,而旧的容器却不能工作。Azure 中的此大小限制是容器还是帐户设置?可以在现有容器上进行非破坏性更改吗?不幸的是,这还没有记录。

您可以使用选项控制此设置,例如

var opts = new BlobClientOptions(BlobClientOptions.ServiceVersion.V2021_12_02);
var client = new AppendBlobClient(uri, creds, opts);
Run Code Online (Sandbox Code Playgroud)

但是,虽然这样设置int AppendBlobClient.AppendBlobMaxAppendBlockBytes,但它本身并不会导致“分块”,您仍然必须像这样附加最大大小的块

文档仍然主要只谈论 4Mb,但请参阅“Azure 存储的版本控制”

最大附加块内容长度已从 4 MiB 提高到 100 MiB。