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

Azure Function(TimerTrigger):Azure文件共享文件复制的并发锁实现

解决Azure Function并发处理文件的锁定方案

针对Azure文件共享中多Function并发处理文件的问题,推荐两种可靠的锁定机制,避免重复处理同一文件:

方案一:使用Azure Files租约(Lease)锁定文件

Azure Files支持排他租约机制,获取租约后,其他进程无法修改、删除或获取该文件的租约,直到租约释放或过期。这是最官方的文件锁定方案。

修改后的代码示例:

for (int i = 0; i < 5; i++)
{
    var file = results2.ToList()[i];

    if (file is CloudFile cloudFile)
    {
        var fileName = cloudFile.Name;
        log.LogInformation($"Function({invocationId}) | Trying to acquire lease for File: {fileName}");

        string leaseId = null;
        try
        {
            // 获取15分钟排他租约(时长可根据操作耗时调整)
            leaseId = await cloudFile.AcquireLeaseAsync(TimeSpan.FromMinutes(15), null);
            log.LogInformation($"Function({invocationId}) | Acquired lease for File: {fileName}, LeaseId: {leaseId}");
        }
        catch (StorageException ex)
        {
            // 409状态码表示租约已被占用,直接跳过该文件
            if (ex.RequestInformation.HttpStatusCode == 409)
            {
                log.LogInformation($"Function({invocationId}) | File {fileName} is locked by another process, skipping");
                continue;
            }
            throw; // 其他异常正常抛出
        }

        try
        {
            log.LogInformation($"Function({invocationId}) | Starting copy for File: {fileName}");

            var copyFile = folderToDir.GetFileReference(fileName);
            await Task.WhenAll(copyFile.CreateAsync(cloudFile.Properties.Length), copyFile.StartCopyAsync(cloudFile));

            log.LogInformation($"Function({invocationId}) | Copied File: {fileName}");
            await cloudFile.DeleteAsync();
        }
        finally
        {
            // 无论成功失败,确保释放租约,避免永久锁定
            if (!string.IsNullOrEmpty(leaseId))
            {
                try
                {
                    await cloudFile.ReleaseLeaseAsync(leaseId);
                    log.LogInformation($"Function({invocationId}) | Released lease for File: {fileName}");
                }
                catch (Exception ex)
                {
                    log.LogWarning($"Function({invocationId}) | Failed to release lease for {fileName}: {ex.Message}");
                }
            }
        }
    }
}

关键说明:

  • 租约获取失败(409冲突)直接跳过,避免重复处理
  • finally块强制释放租约,防止因异常导致文件永久锁定
  • 租约时长需设置为足够完成复制+删除操作的时间

方案二:临时重命名文件(轻量替代方案)

利用Azure Files重命名操作的原子性,处理前将文件重命名为带标记的临时名称,重命名成功则表示获得处理权,失败则说明文件正在被其他进程处理。

修改后的代码示例:

for (int i = 0; i < 5; i++)
{
    var file = results2.ToList()[i];

    if (file is CloudFile cloudFile)
    {
        var fileName = cloudFile.Name;
        var tempFileName = $"{fileName}.processing";
        var tempFile = cloudFile.ParentReference.GetFileReference(tempFileName);
        bool renameSuccess = false;

        log.LogInformation($"Function({invocationId}) | Trying to lock File: {fileName} via rename");

        try
        {
            // 原子性重命名,成功则获得处理权
            await cloudFile.RenameAsync(tempFileName);
            renameSuccess = true;
            log.LogInformation($"Function({invocationId}) | Locked File: {fileName} as {tempFileName}");
        }
        catch (StorageException ex)
        {
            if (ex.RequestInformation.HttpStatusCode == 409)
            {
                log.LogInformation($"Function({invocationId}) | File {fileName} is being processed, skipping");
                continue;
            }
            throw;
        }

        try
        {
            log.LogInformation($"Function({invocationId}) | Starting copy for File: {tempFileName} (original: {fileName})");

            var copyFile = folderToDir.GetFileReference(fileName);
            await Task.WhenAll(copyFile.CreateAsync(tempFile.Properties.Length), copyFile.StartCopyAsync(tempFile));

            log.LogInformation($"Function({invocationId}) | Copied File: {fileName}");
            await tempFile.DeleteAsync();
        }
        catch (Exception ex)
        {
            log.LogError($"Function({invocationId}) | Failed to process {fileName}: {ex.Message}");
            // 处理失败时,将临时文件改回原名称,方便后续重试
            if (renameSuccess)
            {
                await tempFile.RenameAsync(fileName);
            }
        }
    }
}

关键说明:

  • 重命名操作是原子性的,同一时间只有一个进程能成功
  • 处理失败需将临时文件改回原名称,避免文件丢失
  • 实现简单,无需管理租约,适合轻量级场景

额外注意事项

  • 两种方案都需处理异常,确保资源(租约/文件状态)被正确恢复
  • 可结合重试机制,但需限制重试次数,避免无限循环
  • 若使用Azure Function消费计划,需确保函数执行超时时间覆盖操作总耗时

内容的提问来源于stack exchange,提问作者Peace and love

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 20:02:40