处理数千条记录时如何避免PHP脚本超时?
Hey there! Let's tackle this problem head-on—your script hitting a wall at ~400 emails isn't a PHP timeout issue (since you already set set_time_limit(0)), it's almost certainly CloudFlare or Nginx cutting off the long-running HTTP request. Web servers are designed to terminate idle or overly long connections to keep resources free, so running a 2000+ email process through a browser-based script is fighting an uphill battle.
Here are your best solutions, ordered by reliability:
1. 首选方案:用CLI(命令行)运行脚本
This is the gold standard for long-running scripts. When you run PHP via the command line, there's no web server (or CloudFlare) sitting between your script and the network—so no timeouts to worry about.
How to do it:
- SSH into your server, then run your script directly with:
php path/to/your_script.php - If you can't stay logged in to monitor progress, use
nohupto let it run in the background:
This logs all output tonohup php path/to/your_script.php > process.log 2>&1 &process.logso you can check progress later withtail -f process.log. - Bonus: If you need to schedule this regularly, set up a cron job to run the script automatically.
This approach eliminates the CloudFlare/Nginx timeout entirely, and your set_time_limit(0) will actually work as intended.
2. 退而求其次:Web环境下的分批处理(带进度记录)
If you must run this through a web interface (e.g., no SSH access), manually refreshing pages is clunky—instead, use sessions to track your progress and auto-process batches.
Example implementation:
session_start(); // Configure batch size (adjust based on how long each batch takes—keep it under your server's timeout) $batchSize = 150; $emailFile = 'emails.txt'; // Load all emails (only once, or reload each time if the file might change) $emails = file($emailFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); $totalEmails = count($emails); // Get the last processed index from the session $currentIndex = $_SESSION['last_processed'] ?? 0; $endIndex = min($currentIndex + $batchSize, $totalEmails); // Process this batch for ($i = $currentIndex; $i < $endIndex; $i++) { $email = trim($emails[$i]); if (empty($email)) continue; // Run your SMTP validation, CSV marking, and email sending logic here // ... // Optional: Output progress to the browser (since you have ob_implicit_flush enabled) echo "Processed: $email<br>"; } // Update session with the new progress $_SESSION['last_processed'] = $endIndex; // Check if we're done if ($endIndex >= $totalEmails) { unset($_SESSION['last_processed']); echo "<h3>All $totalEmails emails processed successfully!</h3>"; } else { // Auto-refresh to process the next batch (add a small delay to avoid overwhelming the server) echo "<p>Processed $endIndex/$totalEmails emails... refreshing to continue...</p>"; header("Refresh: 2; url=" . $_SERVER['PHP_SELF']); }
Key notes for this approach:
- Adjust
$batchSizeso each batch finishes in under your server's timeout (check Nginx'sproxy_read_timeoutor CloudFlare's "HTTP Request Timeout" setting—usually 60 seconds by default). - Make sure your server allows session persistence (most do, but double-check if you're on a shared host).
3. 通用优化:减少被中断/封禁的概率
No matter which approach you use, these tweaks will make your script more reliable:
- Add small delays between requests: Use
sleep(1)orusleep(500000)(0.5 seconds) after processing each email. This prevents you from hitting rate limits with email providers or triggering CloudFlare's security rules. - Reuse SMTP connections: Instead of creating a new SMTP connection for every email, keep one connection open and reuse it. Most PHP SMTP libraries (like PHPMailer) support this out of the box.
- Add error handling & retries: If a validation/send fails due to a temporary error (e.g., connection timeout), retry 1-2 times before marking it as invalid.
- Log everything: Write detailed logs to a file (e.g.,
error_log("Failed to validate $email: $message", 3, 'process_errors.log');) so you can debug issues later.
内容的提问来源于stack exchange,提问作者Tomek

