You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

并发请求Blob Storage时存在文件却间歇性提示Blob不存在

Azure Blob Storage并发读取时ExistsAsync误判文件不存在的问题解决

问题描述

使用下方代码读取Blob Storage文本文件时,非并发场景完全正常,但同时发送多个请求时,blobClient.ExistsAsync()会在文件实际存在的情况下返回false,最终返回null。需要排查问题并实现同一文本文件的多并发读取支持。

原代码

/// <summary>
/// Extracts the Text content from a file in the Blob Storage.
/// </summary>
/// <param name="blobContainerName">The name of the Container in which the file is present.</param>
/// <param name="blobName">The name of the file to be Read.</param>
/// <returns>
/// File is Present - The Content of the Text File; 
/// File is Empty - An Empty String; 
/// File Not Found - null</returns>
public async Task<string> ReadTextFileFromBlob(string blobContainerName, string blobName)
{
    // Create a BlobServiceClient using account details
    BlobServiceClient blobServiceClient = _blobServiceClient;

    // Get the container client
    BlobContainerClient blobContainerClient = blobServiceClient.GetBlobContainerClient(blobContainerName);

    // Get the blob client
    BlobClient blobClient = blobContainerClient.GetBlobClient(blobName);

    // Check if the blob exists
    if (!await blobClient.ExistsAsync())
    {
        // Return null if the blob does not exist
        return null;
    }
    // Download the blob content as a stream
    using (MemoryStream memoryStream = new MemoryStream())
    {
        await blobClient.DownloadToAsync(memoryStream);

        // Check if the content is empty
        if (memoryStream.Length == 0)
        {
            // Return null if the content is empty
            return "";
        }
        // Convert the stream to a string
        string content = System.Text.Encoding.UTF8.GetString(memoryStream.ToArray());

        return content;
    }
}

问题根源

原代码中ExistsAsync()和DownloadToAsync()是两个独立的HTTP请求,存在竞态条件:

  1. 并发场景下,两次请求的时间窗口内可能出现临时网络波动,导致ExistsAsync()请求失败返回false;
  2. 即使ExistsAsync()返回true,后续下载请求仍可能因文件被删除、权限变更等原因失败,两次分离的请求无法保证原子性;
  3. Azure Blob客户端默认的重试策略可能在并发请求时未正确处理部分异常,导致误判文件不存在。

此外,原代码使用MemoryStream增加了内存开销,并发场景下会加剧资源消耗。

解决方案

方案1:直接下载+捕获404异常(推荐)

取消ExistsAsync()检查,直接尝试下载文件,通过捕获404异常判断文件是否存在。这种方式将两次请求合并为一次,消除竞态条件,同时提升性能。

修改后的代码:

/// <summary>
/// Extracts the Text content from a file in the Blob Storage.
/// </summary>
/// <param name="blobContainerName">The name of the Container in which the file is present.</param>
/// <param name="blobName">The name of the file to be Read.</param>
/// <returns>
/// File is Present - The Content of the Text File; 
/// File is Empty - An Empty String; 
/// File Not Found - null</returns>
public async Task<string> ReadTextFileFromBlob(string blobContainerName, string blobName)
{
    BlobContainerClient blobContainerClient = _blobServiceClient.GetBlobContainerClient(blobContainerName);
    BlobClient blobClient = blobContainerClient.GetBlobClient(blobName);

    try
    {
        // 直接下载Blob内容,无需提前检查存在性
        var downloadResponse = await blobClient.DownloadContentAsync();
        string content = downloadResponse.Value.Content.ToString();
        
        // 处理空内容场景
        return string.IsNullOrWhiteSpace(content) ? "" : content;
    }
    catch (RequestFailedException ex) when (ex.Status == 404)
    {
        // 404状态码表示文件不存在,返回null
        return null;
    }
}

方案2:保留Exists检查并优化请求一致性

如果必须保留存在性检查,可通过设置BlobRequestConditions增强请求的一致性,同时确保客户端实例的线程安全:

  1. 确保_blobServiceClient是单例实例(通过DI容器注册为Singleton),BlobServiceClient、BlobContainerClient、BlobClient均为线程安全类型,单例复用可避免连接池问题;
  2. 在ExistsAsync()中添加请求条件,避免缓存或 stale 响应:
var existsOptions = new BlobExistsOptions
{
    Conditions = new BlobRequestConditions
    {
        // 禁用缓存,强制从服务端获取最新状态
        IfNoneMatch = ETag.All
    }
};

if (!await blobClient.ExistsAsync(existsOptions))
{
    return null;
}

额外优化点

  • 避免使用MemoryStream,DownloadContentAsync()直接返回Blob内容的字符串表示,减少内存分配和流操作开销;
  • 确保客户端配置了合理的重试策略,可在创建BlobServiceClient时设置BlobClientOptions的Retry属性,应对临时网络波动:
var blobClientOptions = new BlobClientOptions
{
    Retry =
    {
        Delay = TimeSpan.FromSeconds(2),
        MaxDelay = TimeSpan.FromSeconds(10),
        MaxRetries = 3,
        Mode = RetryMode.Exponential
    }
};

var blobServiceClient = new BlobServiceClient(connectionString, blobClientOptions);

关键说明

Azure Blob Storage本身支持高并发读取,同一Blob可被多个客户端同时访问,问题并非来自存储服务本身,而是原代码中分离的存在性检查与下载操作导致的竞态条件。采用直接下载+异常捕获的方式能从根本上避免此类问题。

内容的提问来源于stack exchange,提问作者Gangadhar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 21:51:01