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

仅在Azure Web App中FTP文件下载失败问题求助

排查Azure Web Job中FTP下载挂起的问题

结合你描述的现象——本地运行正常、Azure环境下少量文件后挂起,且换WebClient、移除存储上传都无法解决,大概率是连接资源耗尽或者环境层面的连接限制导致的,以下是具体的排查方向和解决方案:

1. 强制关闭FTP连接,避免连接池耗尽

FtpWebRequest默认会启用KeepAlive = true,尝试复用连接,但Azure Web Job的宿主环境中,连接复用可能导致连接没有正确释放,最终连接池被占满,后续请求无法获取连接而挂起。

修改代码,强制关闭连接复用:

var ftpRequest = (FtpWebRequest)FtpWebRequest.Create(fileUri);
ftpRequest.KeepAlive = false; // 关键:每个请求完成后立即关闭连接
// 保留你原有的其他配置
ftpRequest.Method = WebRequestMethods.Ftp.DownloadFile;
ftpRequest.Credentials = new NetworkCredential("***", "***");
ftpRequest.UseBinary = false;
ftpRequest.Timeout = 10_000;
ftpRequest.ReadWriteTimeout = 10_000;

2. 限制并发下载数,避免触发FTP服务器/Azure的连接限制

很多FTP服务器会限制同一IP的并发连接数,Azure Web Job的出站连接也有默认限制(比如免费层并发连接数较低)。如果你的代码是并行下载多个文件,很容易触发这些限制,导致服务器不再响应新请求(但不会返回错误,造成挂起)。

用SemaphoreSlim控制并发数,比如限制为2-3个同时下载:

// 初始化信号量,限制最大并发数为2
var downloadSemaphore = new SemaphoreSlim(2);

// 遍历文件时用信号量控制并发
foreach (var fileUri in files) {
    await downloadSemaphore.WaitAsync().ConfigureAwait(false);
    try {
        // 你的FTP下载代码
        var ftpRequest = (FtpWebRequest)FtpWebRequest.Create(fileUri);
        ftpRequest.KeepAlive = false;
        // ... 其他配置
        using (WebResponse response = await ftpRequest.GetResponseAsync().ConfigureAwait(false))
        using (var stream = response.GetResponseStream()) {
            await CopyAsync(stream, container, GetFilename(fileUri), false).ConfigureAwait(false);
        }
    } catch (Exception e) {
        Logger.Error(Unknow, $"Error downloading {fileUri.AbsoluteUri}:\n{e}");
    } finally {
        downloadSemaphore.Release(); // 释放信号量,允许下一个请求执行
    }
}

3. 完善超时取消逻辑,避免僵尸请求占用资源

你之前尝试的超时包装可能没有正确终止FTP请求,导致后台存在大量未完成的僵尸请求,占用资源并逐渐恶化。改用CancellationTokenSource绑定超时,并在超时后主动终止请求:

foreach (var fileUri in files) {
    await downloadSemaphore.WaitAsync().ConfigureAwait(false);
    try {
        using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); // 10秒超时
        var ftpRequest = (FtpWebRequest)FtpWebRequest.Create(fileUri);
        ftpRequest.KeepAlive = false;
        // ... 其他配置
        
        // 将取消令牌传入异步方法
        using (WebResponse response = await ftpRequest.GetResponseAsync(cts.Token).ConfigureAwait(false))
        using (var stream = response.GetResponseStream()) {
            await CopyAsync(stream, container, GetFilename(fileUri), false).ConfigureAwait(false);
        }
    } catch (OperationCanceledException) {
        Logger.Error(Unknow, $"Download timed out for {fileUri.AbsoluteUri}");
    } catch (Exception e) {
        Logger.Error(Unknow, $"Error downloading {fileUri.AbsoluteUri}:\n{e}");
    } finally {
        downloadSemaphore.Release();
    }
}

4. 排查环境层面的限制

  • 检查Azure定价层:如果使用免费/共享层,升级到基本层测试,这类层级有更严格的资源和连接限制,可能导致请求挂起。
  • 切换FTP模式:尝试修改ftpRequest.UsePassive = false(主动模式),有些Azure环境对被动模式的FTP端口范围有限制,导致连接无法建立。
  • 查看FTP服务器日志:联系FTP服务器管理员,确认是否有针对Azure出口IP的连接限制、异常断开记录,或者服务器端的连接数已满。
  • 查看Azure诊断日志:在Azure门户中开启Web Job的诊断日志,查看是否有网络相关的警告、连接限制触发的提示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:17:03