使用PHP mail函数发送2400封邮件遇阻,求分批发送方案
嘿,一次性发送2400封邮件确实容易踩坑——不管是Web服务器超时、主机商的邮件发送限制,还是被邮件服务商当成垃圾邮件拦截,分批发送绝对是更稳妥的选择。甚至结合队列系统会让整个流程更可控,我来给你拆解几种方案,顺便优化你的现有代码:
基础分批改造(快速见效)
如果你想快速修改现有代码实现分批发送,可以通过URL参数记录发送进度,每次请求处理固定数量的邮件(比如10封)。这样能避免单次请求超时,也降低被拦截的风险。
下面是修改后的代码,我做了这些优化:
- 新增批次控制逻辑,通过
offset参数记录已发送的位置 - 修复了原代码中拼写错误的
UFT-8编码(改为UTF-8) - 添加邮箱格式验证,跳过无效地址
- 用更精细的
usleep()代替sleep(),灵活控制发送间隔 - 记录失败邮件,方便后续重试
<?php include('../../includingThis.php'); $fra = "ME <me@me.com>"; $emne = ""; $sql = "SELECT * FROM sendThese WHERE id=1"; $til = []; $message = ""; $besked = ""; if ($result = mysqli_query($con, $sql)) { while($row = mysqli_fetch_assoc($result)) { $emne = $row["subject"]; $message = $row["message"]; $mailsHere = $row["mailsHere"]; $til = explode(",", $mailsHere); } } // 分批配置:每次发送10封 $batchSize = 10; $offset = isset($_GET['offset']) ? (int)$_GET['offset'] : 0; $totalMails = count($til); $endIndex = min($offset + $batchSize, $totalMails); $besked = file_get_contents('sendThese.html'); $besked = str_replace("%%message%%", $message, $besked); // 修复编码拼写错误,添加标准换行符 $headers = "MIME-Version:1.0\n"; $headers .= "Content-type: text/html; charset=UTF-8\n"; $headers .= "From: ".$fra."\n"; $mailsSend = 0; $failedMails = []; // 处理当前批次的邮箱 for ($i = $offset; $i < $endIndex; $i++) { $email = trim($til[$i]); // 跳过无效邮箱格式 if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { $failedMails[] = $email . " (无效格式)"; continue; } // 发送邮件(这里可以替换为PHPMailer更可靠) $sent = mail($email, $emne, $besked, $headers); if ($sent) { $mailsSend++; echo $email . " 发送成功<br>"; } else { $failedMails[] = $email; echo $email . " 发送失败<br>"; } // 间隔0.5秒,避免触发反垃圾机制(可根据服务商调整) usleep(500000); } // 输出批次结果 echo "<br>=== 批次总结 ===<br>"; echo "成功发送:" . $mailsSend . " 封<br>"; echo "发送失败:" . count($failedMails) . " 封<br>"; if (!empty($failedMails)) { echo "失败列表:<br>" . implode("<br>", $failedMails) . "<br>"; } // 生成下一批次链接 if ($endIndex < $totalMails) { $nextOffset = $endIndex; $remaining = $totalMails - $endIndex; echo "<a href='?offset=" . $nextOffset . "'>点击发送下一批(剩余 " . $remaining . " 封)</a>"; } else { echo "<br>🎉 所有邮件处理完成!总共 " . $totalMails . " 封"; } // 可选:将失败邮件存入数据库,方便后续重试 // if (!empty($failedMails)) { // $failedList = mysqli_real_escape_string($con, implode(",", $failedMails)); // mysqli_query($con, "INSERT INTO failed_mails (emails, subject, created_at) VALUES ('$failedList', '$emne', NOW())"); // } ?>
进阶异步队列方案(专业可靠)
如果你的发送需求比较频繁,或者不想手动点击批次链接,推荐用CLI脚本+定时任务的异步方案。这种方式脱离Web服务器的超时限制,能自动后台处理邮件。
步骤1:修改数据库表
在sendThese表中新增sent_count字段(INT类型,默认0),用来记录已发送的邮件数量,避免重复发送。
步骤2:编写CLI处理脚本
<?php include('../../includingThis.php'); $fra = "ME <me@me.com>"; $emne = ""; $sql = "SELECT * FROM sendThese WHERE id=1"; $til = []; $message = ""; $besked = ""; if ($result = mysqli_query($con, $sql)) { while($row = mysqli_fetch_assoc($result)) { $emne = $row["subject"]; $message = $row["message"]; $mailsHere = $row["mailsHere"]; $til = explode(",", $mailsHere); $sentCount = $row["sent_count"] ?? 0; } } $totalMails = count($til); if ($sentCount >= $totalMails) { echo date('Y-m-d H:i:s') . " 所有邮件已发送完成!\n"; exit; } // 每次处理10封 $batchSize = 10; $startIndex = $sentCount; $endIndex = min($startIndex + $batchSize, $totalMails); $besked = file_get_contents('sendThese.html'); $besked = str_replace("%%message%%", $message, $besked); $headers = "MIME-Version:1.0\n"; $headers .= "Content-type: text/html; charset=UTF-8\n"; $headers .= "From: ".$fra."\n"; $mailsSend = 0; $failedMails = []; for ($i = $startIndex; $i < $endIndex; $i++) { $email = trim($til[$i]); if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { $failedMails[] = $email; continue; } $sent = mail($email, $emne, $besked, $headers); if ($sent) { $mailsSend++; echo date('Y-m-d H:i:s') . " 成功:" . $email . "\n"; } else { $failedMails[] = $email; echo date('Y-m-d H:i:s') . " 失败:" . $email . "\n"; } usleep(500000); } // 更新已发送数量到数据库 $newSentCount = $sentCount + $mailsSend; mysqli_query($con, "UPDATE sendThese SET sent_count = $newSentCount WHERE id=1"); // 记录失败邮件 if (!empty($failedMails)) { $failedList = mysqli_real_escape_string($con, implode(",", $failedMails)); mysqli_query($con, "INSERT INTO failed_mails (emails, subject, created_at) VALUES ('$failedList', '$emne', NOW())"); } echo "\n本次处理完成:成功 " . $mailsSend . " 封,失败 " . count($failedMails) . " 封。剩余 " . ($totalMails - $newSentCount) . " 封\n"; ?>
步骤3:设置定时任务(Cron)
在服务器上添加Cron任务,每分钟运行一次脚本,自动处理批次:
* * * * * php /path/to/your/mail_script.php >> /path/to/mail_logs/send.log 2>&1
关键优化细节
- 替换原生
mail()函数:推荐用PHPMailer或SwiftMailer,它们支持SMTP认证,错误处理更完善,还能避免很多原生函数的兼容性问题。 - 遵守发送限制:不同邮件服务商有不同的发送频率限制(比如Gmail每天500封,每分钟100封),一定要调整间隔时间,避免被封号或拦截。
- 添加退订机制:符合GDPR、CAN-SPAM等法规要求,减少被标记为垃圾邮件的概率。
- 测试先行:先给少量测试邮箱发送,确认内容、格式和发送正常后,再启动批量发送。
内容的提问来源于stack exchange,提问作者FeReTu
相关产品推荐
相关产品推荐

