1. PHP CLI模式与MySQL队列实现邮件批量发送实战
在Linux环境下使用PHP CLI模式结合MySQL实现异步任务处理是一个经典的生产力工具组合。这种方案特别适合处理耗时操作如批量邮件发送、事件通知等场景,能够有效减轻Web服务器的即时响应压力。
1.1 核心架构设计
整个系统由三个关键组件构成:
- 任务生产者:Web应用将待发送邮件存入MySQL数据库
- 任务消费者:PHP CLI脚本从数据库读取待处理任务
- 执行环境:Linux系统提供稳定的后台运行支持
这种架构的优势在于:
- 解耦Web请求与耗时操作
- 利用MySQL的事务特性保证任务不丢失
- 通过Linux进程管理实现可靠的后台执行
1.2 数据库表设计
邮件队列需要至少包含以下字段:
CREATE TABLE `task_list` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_email` varchar(255) NOT NULL, `subject` varchar(255) NOT NULL, `content` text NOT NULL, `status` tinyint(1) DEFAULT '0' COMMENT '0-待发送 1-已发送 2-发送失败', `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;注意:status字段使用枚举值而非布尔值,为后续扩展留有余地。比如可以增加"重试中"等中间状态。
2. 核心实现细节解析
2.1 CLI脚本的健壮性设计
一个生产环境可用的CLI脚本需要考虑以下方面:
#!/usr/bin/env php <?php // 防止脚本被重复执行 $lockFile = '/tmp/mail_queue.lock'; if (file_exists($lockFile)) { exit("Another process is running\n"); } file_put_contents($lockFile, getmypid()); // 注册信号处理器 pcntl_signal(SIGTERM, function() use ($lockFile) { unlink($lockFile); exit; }); // 异常处理 register_shutdown_function(function() use ($lockFile) { if (file_exists($lockFile)) { unlink($lockFile); } }); // 主业务逻辑 while (true) { processMailQueue(); sleep(5); // 控制处理频率 }关键点说明:
- 文件锁防止脚本重复执行
- 信号处理确保优雅退出
- 异常处理保证锁文件被清理
- 循环间隔避免CPU占用过高
2.2 邮件发送模块实现
使用PHPMailer发送邮件时的优化技巧:
function sendEmail($address, $subject, $content) { $mail = new PHPMailer(true); try { // SMTP配置 $mail->isSMTP(); $mail->Host = 'smtp.example.com'; $mail->SMTPAuth = true; $mail->Username = 'user@example.com'; $mail->Password = 'password'; $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; $mail->Port = 587; // 超时设置 $mail->Timeout = 15; $mail->SMTPKeepAlive = true; // 邮件内容 $mail->setFrom('noreply@example.com', 'System'); $mail->addAddress($address); $mail->Subject = $subject; $mail->Body = $content; return $mail->send(); } catch (Exception $e) { // 记录详细错误日志 error_log("Mail send failed: " . $e->getMessage()); return false; } }提示:SMTPKeepAlive可以在批量发送时复用连接,显著提升性能。实测发送100封邮件,开启KeepAlive可将总耗时从60秒降至20秒左右。
3. 完整实现流程
3.1 生产者端实现
Web应用中添加邮件到队列:
// 使用事务保证数据一致性 Db::startTrans(); try { $data = [ 'user_email' => 'user@example.com', 'subject' => '欢迎邮件', 'content' => '感谢注册我们的服务', 'status' => 0 ]; Db::name('task_list')->insert($data); Db::commit(); } catch (\Exception $e) { Db::rollback(); // 记录错误日志 }3.2 消费者端实现
CLI脚本处理队列的核心逻辑:
function processMailQueue() { // 每次处理100条,避免内存占用过高 $tasks = Db::name('task_list') ->where('status', 0) ->limit(100) ->select(); foreach ($tasks as $task) { try { $result = sendEmail($task['user_email'], $task['subject'], $task['content']); // 更新状态 Db::name('task_list') ->where('id', $task['id']) ->update([ 'status' => $result ? 1 : 2, 'updated_at' => date('Y-m-d H:i:s') ]); // 控制发送频率 usleep(200000); // 200ms间隔 } catch (\Exception $e) { // 记录错误日志 } } }3.3 Linux环境部署
使用supervisor管理CLI进程:
[program:mail_queue] command=/usr/bin/php /path/to/mail_queue.php directory=/path/to/ user=www-data autostart=true autorestart=true redirect_stderr=true stdout_logfile=/var/log/mail_queue.log stdout_logfile_maxbytes=10MB stdout_logfile_backups=5启动命令:
sudo supervisorctl reread sudo supervisorctl update sudo supervisorctl start mail_queue4. 常见问题与优化方案
4.1 性能瓶颈排查
当处理速度不理想时,可以检查以下方面:
数据库查询效率:
EXPLAIN SELECT * FROM task_list WHERE status = 0 LIMIT 100;确保status字段有索引
SMTP服务器响应:
telnet smtp.example.com 587测试SMTP服务器连接速度
PHP执行限制:
; php.ini配置 max_execution_time = 0 memory_limit = 256M
4.2 邮件发送失败处理
对于发送失败的邮件,建议实现以下机制:
重试策略:
// 在update语句中增加重试次数字段 ->update([ 'status' => 2, 'retry_count' => Db::raw('retry_count+1'), 'next_retry_time' => date('Y-m-d H:i:s', time() + 3600) // 1小时后重试 ]);死信队列:超过最大重试次数(如3次)后归档到单独表
报警机制:连续失败达到阈值时触发报警
4.3 高并发优化
当邮件量很大时(>1万/天),可以考虑:
多进程处理:
// 启动5个worker进程 for ($i = 0; $i < 5; $i++) { $pid = pcntl_fork(); if ($pid == 0) { // 子进程处理逻辑 processMailQueue(); exit; } }分表策略:按日期或用户ID哈希分表
引入Redis:使用List结构作为缓冲队列
5. 监控与维护
5.1 基础监控指标
队列积压量:
SELECT COUNT(*) FROM task_list WHERE status = 0;处理速度:
# 查看日志统计 grep "Processed" /var/log/mail_queue.log | wc -l失败率:
SELECT COUNT(*) FROM task_list WHERE status = 2;
5.2 日志分析技巧
有效的日志应包含:
- 处理开始/结束时间
- 每次处理的任务数量
- 异常情况的堆栈跟踪
- 性能关键点的耗时统计
示例日志格式:
[2023-08-20 14:00:00] INFO: Started processing [2023-08-20 14:00:05] INFO: Processed 50 emails in 5.2s [2023-08-20 14:00:10] ERROR: Failed to send to user@example.com - Connection timed out5.3 灾备方案
确保系统可靠性的措施:
数据库定期备份:
mysqldump -u user -p task_queue > backup.sql脚本异常重启:
; supervisor配置 autorestart = true startretries = 3监控脚本存活:
# 定时任务检查 */5 * * * * pgrep -f mail_queue.php || supervisorctl start mail_queue
在实际项目中,这种基于PHP CLI和MySQL的队列系统已经稳定支持了我们日均10万+邮件的发送需求。关键是要处理好异常情况,建立完善的监控体系,才能保证长期稳定运行。对于更高并发的场景,可以考虑引入专业的队列系统如RabbitMQ,但MySQL方案在中小规模下仍然是最简单可靠的选择。