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
相关产品推荐
相关产品推荐

