PHP Mailer发送邮件致网站崩溃,如何转为Cron Job运行?
解决网站发送邮件崩溃问题:改用Cron Job后台处理邮件发送
看起来你的网站崩溃是因为邮件发送过程阻塞了用户请求——PHP默认是同步执行的,发送邮件时要等SMTP服务器响应,这段时间网站进程被卡住,高并发下很容易崩溃。把邮件发送移到Cron Job后台运行是非常正确的思路,我一步步帮你改:
第一步:重构代码逻辑,引入邮件任务表
Cron Job是定时后台运行的脚本,没法直接接收用户的POST请求,所以我们需要先把"发送邮件"的请求变成数据库里的待处理任务,再让Cron脚本定时去读取并执行。
1. 创建邮件任务表
先在你的数据库里建一张表,用来存待发送的邮件:
CREATE TABLE pending_emails ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL, subject VARCHAR(255) NOT NULL, body TEXT NOT NULL, status ENUM('pending', 'sent', 'failed') DEFAULT 'pending', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
2. 修改原前端提交代码(不再直接发邮件)
原来的代码是用户提交后直接发邮件,现在改成把任务插入到上面的表(同时修复了原代码的SQL注入漏洞):
<?php define('DB_NAME', 'daniel_login'); define('DB_USER', 'daniel_login'); define('DB_PASSWORD', '#####'); define('DB_HOST', 'localhost'); $conn = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME); if (!$conn) { die('Could not connect: ' . mysqli_connect_error()); } if (isset($_POST['recovery'])) { $recovery = $_POST['recovery']; // 用预处理语句查询用户邮箱,避免SQL注入 $stmt = $conn->prepare("SELECT email FROM members WHERE username = ?"); $stmt->bind_param("s", $recovery); $stmt->execute(); $result = $stmt->get_result(); if ($row = $result->fetch_assoc()) { $user_email = $row['email']; $subject = "密码找回通知"; // 替换成你的邮件主题 $body = "您好,点击下方链接重置密码:\n[你的重置链接]"; // 替换成实际邮件内容 // 插入待发送任务到数据库 $stmt = $conn->prepare("INSERT INTO pending_emails (username, email, subject, body) VALUES (?, ?, ?, ?)"); $stmt->bind_param("ssss", $recovery, $user_email, $subject, $body); $stmt->execute(); echo "找回邮件已安排发送,请查收邮箱"; } else { echo "该用户名不存在"; } $stmt->close(); } $conn->close(); ?>
第二步:写Cron Job专用的邮件发送脚本
创建一个独立的脚本,比如send_pending_emails.php,建议放在非Web根目录(防止被公开访问):
<?php define('DB_NAME', 'daniel_login'); define('DB_USER', 'daniel_login'); define('DB_PASSWORD', '#####'); define('DB_HOST', 'localhost'); $conn = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME); if (!$conn) { die('Could not connect: ' . mysqli_connect_error()); } // 一次处理10封待发邮件,避免脚本超时 $stmt = $conn->prepare("SELECT * FROM pending_emails WHERE status = 'pending' LIMIT 10"); $stmt->execute(); $result = $stmt->get_result(); require 'PHPMailer-master/PHPMailerAutoload.php'; while ($row = $result->fetch_assoc()) { $mail = new PHPMailer(); // 配置你的SMTP服务器信息(必填) $mail->isSMTP(); $mail->Host = 'smtp.your-email-provider.com'; // 比如smtp.gmail.com $mail->SMTPAuth = true; $mail->Username = 'your-account@example.com'; $mail->Password = 'your-email-password'; $mail->SMTPSecure = 'tls'; $mail->Port = 587; $mail->setFrom('your-account@example.com', '你的网站名称'); $mail->addAddress($row['email'], $row['username']); $mail->Subject = $row['subject']; $mail->Body = $row['body']; // 发送后更新任务状态 if ($mail->send()) { $update_stmt = $conn->prepare("UPDATE pending_emails SET status = 'sent' WHERE id = ?"); $update_stmt->bind_param("i", $row['id']); $update_stmt->execute(); $update_stmt->close(); } else { $update_stmt = $conn->prepare("UPDATE pending_emails SET status = 'failed' WHERE id = ?"); $update_stmt->bind_param("i", $row['id']); $update_stmt->execute(); $update_stmt->close(); // 记录错误日志,方便排查 error_log("邮件发送失败(ID: {$row['id']}):" . $mail->ErrorInfo); } } $stmt->close(); $conn->close(); ?>
第三步:配置Cron Job定时运行脚本
Cron是Linux服务器的定时任务工具,配置方法如下:
- 登录服务器终端,输入
crontab -e编辑定时任务列表。 - 添加一行配置(替换成你的脚本实际路径),比如每5分钟运行一次:
*/5 * * * * php /home/your-user/send_pending_emails.php >> /var/log/email_cron.log 2>&1
参数解释:
*/5 * * * *:表示每5分钟执行一次(可按需修改:* * * * *每分钟,0 * * * *每小时)php /home/your-user/send_pending_emails.php:脚本的绝对路径>> /var/log/email_cron.log 2>&1:将运行日志写入文件,方便后续排查问题
- 保存退出后,Cron会自动生效。
关键注意事项
- 安全优先:原代码直接拼接用户输入到SQL的写法有严重注入风险,重构后的预处理语句一定要保留!
- SMTP配置:必须在Cron脚本里填写正确的SMTP服务器信息,否则邮件无法发送。
- 权限检查:确保脚本有执行权限,且PHP能访问到PHPMailer的文件。
- 日志监控:定期查看
/var/log/email_cron.log,如果有失败邮件可以及时排查。
内容的提问来源于stack exchange,提问作者Daniel Faulkner
相关产品推荐
相关产品推荐

