
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) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;注意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 userexample.com; $mail-Password password; $mail-SMTPSecure PHPMailer::ENCRYPTION_STARTTLS; $mail-Port 587; // 超时设置 $mail-Timeout 15; $mail-SMTPKeepAlive true; // 邮件内容 $mail-setFrom(noreplyexample.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 userexample.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/ userwww-data autostarttrue autorestarttrue redirect_stderrtrue stdout_logfile/var/log/mail_queue.log stdout_logfile_maxbytes10MB stdout_logfile_backups5启动命令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 256M4.2 邮件发送失败处理对于发送失败的邮件建议实现以下机制重试策略// 在update语句中增加重试次数字段 -update([ status 2, retry_count Db::raw(retry_count1), 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 userexample.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方案在中小规模下仍然是最简单可靠的选择。