使用 AppendTextAsync 追加到 Azure Append Blob 会导致数据丢失

Append to Azure Append Blob Using AppendTextAsync Results in Missing Data

我正在尝试使用新的 Azure 附加 blob 和 Azure 存储 SDK 6.0.0 为 Azure 中的应用程序创建记录器。因此,我创建了一个快速测试应用程序,以更好地了解附加 blob 及其性能特征。

我的测试程序简单地循环了 100 次并将一行文本附加到附加 blob。如果我使用同步 AppendText() 方法一切正常,但是,它似乎仅限于每秒写入大约 5-6 个追加。所以我尝试使用异步AppendTextAsync()方法;然而,当我使用这种方法时,循环运行得更快(正如预期的那样),但是追加 blob 丢失了大约 98% 的追加文本,没有任何异常被抛出。

如果我添加一个 Thread.Sleep 并在每次追加操作之间休眠 100 毫秒,我最终会得到大约 50% 的数据。休眠1秒,我得到所有数据。

这似乎类似于在 v5.0.0 中发现但在 v5.0.2 中修复的问题:https://github.com/Azure/azure-storage-net/releases/tag/v5.0.2

如果您想重现此问题,这是我的测试代码:

static void Main(string[] args)
{
    var accountName = "<account-name>";

    var accountKey = "<account-key>;

    var credentials = new StorageCredentials(accountName, accountKey);

    var account = new CloudStorageAccount(credentials, true);

    var client = account.CreateCloudBlobClient();

    var container = client.GetContainerReference("<container-name>");

    container.CreateIfNotExists();

    var blob = container.GetAppendBlobReference("append-blob.txt");

    blob.CreateOrReplace();

    for (int i = 0; i < 100; i++)
        blob.AppendTextAsync(string.Format("Appending log number {0} to an append blob.\r\n", i));

    Console.WriteLine("Press any key to exit.");

    Console.ReadKey();
}

有谁知道我尝试将文本行附加到附加 blob 时是否做错了什么?否则,知道为什么这会丢失数据而不抛出某种异常吗?

我真的很想开始使用它作为我的应用程序日志的存储库(因为它主要是为此目的创建的)。但是,如果日志记录速度超过每秒 5-6 条日志,如果日志在没有警告的情况下丢失,那将是非常不可靠的。

如有任何想法或反馈,我们将不胜感激。

您使用的 async 方法不正确。 blob.AppendTextAsync() 是非阻塞的,但是当它 returns 时并没有真正完成。在退出进程之前,您应该等待所有 async 个任务。

以下代码是正确的用法:

var tasks = new Task[100];
for (int i = 0; i < 100; i++)
    tasks[i] = blob.AppendTextAsync(string.Format("Appending log number {0} to an append blob.\r\n", i));

Task.WaitAll(tasks);

Console.WriteLine("Press any key to exit.");

Console.ReadKey();

根据@ZhaoxingLu-Microsoft 提供的信息,我现在有了一个可行的解决方案。根据 API 文档,AppendTextAsync() 方法只能用于 single-writer 场景,因为 API 内部使用 append-offset 条件 header 以避免在 multiple-writer 场景中不起作用的重复块。

这是指定此行为是设计使然的文档: https://msdn.microsoft.com/en-us/library/azure/mt423049.aspx

所以解决方法是改用AppendBlockAsync()方法。以下实现似乎可以正常工作:

for (int i = 0; i < 100; i++)
{
    var message = string.Format("Appending log number {0} to an append blob.\r\n", i);

    var bytes = Encoding.UTF8.GetBytes(message);

    var stream = new MemoryStream(bytes);

    tasks[i] = blob.AppendBlockAsync(stream);              
}

Task.WaitAll(tasks);

请注意,我没有在这个例子中明确处理内存流,因为该解决方案需要一个 using 块,在 using 块内有一个 async/await 以等待异步追加操作在处理内存流之前完成...但这会导致完全不相关的问题。