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

PHP FTP上传因30秒响应超时引发重复执行问题求助

解决FTP上传超时导致请求重复执行的问题

问题背景

我搭建了一个简易FTP文件上传系统,当前遇到的核心问题是:FTP服务器响应耗时可能达到30秒,一旦响应超过30秒,就会触发新的进程,导致待上传文件重复。从上传成功后的日志可以看到,30秒后会生成第二条重复日志,之后无其他日志输出。我需要让请求完整执行,等待FTP响应后再进行后续处理,避免重复执行。

当前代码如下:

$filename = date_format(date_create(), 'Y-m-d-H-i-s-u').'.txt';
$filePath = 'tmp\' . $fileName;
$ediFile = fopen($filePath, 'w');
fwrite($ediFile, $fileContents);
fclose($ediFile);
$log = @$sftp->uploadFile($fileName, $filePath); // 该操作耗时约30秒才会返回响应
unlink($filePath);
$log['id'] = $id;
$log['Invoice ID'] = $invoiceId;

$this->log($log, 'availity');
sleep(30); // 测试用,暂时禁用uploadFile避免频繁请求服务器

日志显示代码会在30秒节点重复执行一次。我已经尝试在控制器函数和上传函数中设置set_time_limit(0);,但代码仍会在30秒后重新执行。我的需求是:上传操作仅执行一次,若超时则直接抛出504错误,而非重试整个函数。


解决方案

1. 检查并调整Web服务器超时配置

set_time_limit(0)仅控制PHP脚本的执行时间,但Web服务器(如Nginx、Apache)自身的请求超时限制才是30秒后触发重试的核心原因:

  • Nginx:修改fastcgi_read_timeout或proxy_read_timeout配置,将超时时间调整为大于FTP上传的预期耗时(比如60秒):
    location ~ \.php$ {
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_read_timeout 60s; # 延长超时时间
    }
    
  • Apache:调整Timeout指令(若使用反向代理,需同时修改ProxyTimeout):
    Timeout 60
    

2. 禁用服务器/负载均衡的自动重试机制

如果Web服务器或前端负载均衡器(如AWS ALB、Cloudflare)配置了超时自动重试规则,必须关闭该功能:

  • Nginx中确保proxy_next_upstream未包含超时相关的重试条件;
  • 云服务负载均衡控制台中,关闭对超时请求的自动重试策略。

3. 为FTP上传设置明确超时并主动抛出504

在SFTP客户端的上传逻辑中设置超时时间,捕获超时异常后直接返回504错误,避免无限制等待:

// 以phpseclib为例,设置SFTP超时
$sftp->setTimeout(60); // 设置60秒超时阈值
try {
    $log = $sftp->uploadFile($fileName, $filePath); // 去掉@错误抑制,便于捕获异常
} catch (Exception $e) {
    // 记录错误日志并抛出504
    $this->log(['error' => $e->getMessage(), 'id' => $id], 'availity_error');
    http_response_code(504);
    exit;
}

4. 移除错误抑制符@

代码中使用@会屏蔽uploadFile的错误输出,导致无法及时排查上传异常,建议用try-catch替代错误抑制,便于定位问题。

5. 幂等性兜底方案(防止极端情况重复上传)

如果无法彻底避免重试,可通过幂等性校验防止重复文件:

  • 用业务唯一标识(如$invoiceId)生成文件名;
  • 上传前检查FTP服务器是否已存在该文件,存在则直接跳过:
$filename = $invoiceId . '_' . date_format(date_create(), 'Y-m-d-H-i-s-u').'.txt';
// 上传前校验文件是否已存在
if ($sftp->fileExists($filename)) {
    $log['status'] = 'skipped (file exists)';
    $this->log($log, 'availity');
    exit;
}
// 执行上传逻辑...

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 09:23:24