活力39282
在线时间13640 小时
阅读权限200
管理员
自由的灵魂
- 积分
- 107434
- 主题
- 5630
- 回帖
- 26661
- 注册时间
- 2003-4-10
- 最后登录
- 2026-10-2
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
1、问题缘起
论坛后台“用户 → 发送通知”功能支持按用户组、积分、注册时间等条件筛选会员并批量发送邮件。原本是同步发送:每发一封邮件都会等待 SMTP 响应,再处理下一封。
核心问题:
| 问题 | 说明 | | SMTP 并发限额 | QQ 域名邮箱短时间内最多允许 10 个并列连接,超过后域名邮箱会拒发(临时封禁) | | 同步阻塞 | 群发时页面需等待所有邮件发送完毕,收件人多时直接卡死或超时 | | 无失败重试 | 发送失败仅记录日志,无法自动重试 |
独立问题汇总:
| 序号 | 问题 | 结论 | | 1 | 后台群发邮件为何同步阻塞? | notifymembers() 函数中直接调用 sendmail() | | 2 | QQ 邮箱为何拒发? | 短时间超过 10 个并列连接,触发服务商频率限制 | | 3 | 能否复用现有邮件队列? | 可以,系统已有 common_mail_queue 队列表和 Crontab 处理脚本 | | 4 | 邮件内容中的 {username}、{regdate} 为何没替换? | 后台群发不支持模板变量,需手动替换 | | 5 | 邮件中 mailto: 链接为何异常? | 邮件系统对 <html> 标签处理异常,导致 mailto: 被拼接为相对路径 |
2、解决方案
(1)简要技术分析:
在后台群发邮件的 email 分支中,将同步 sendmail() 替换为写入邮件队列表,由 Crontab 定时任务异步逐封发送,彻底避开 QQ 邮箱的并列连接限制。
(2)修改文件列表:
| 操作 | 文件路径 | | 修改 | /source/admincp/admincp_members.php |
(3)关键代码段:
定位:搜索 } elseif($_GET['notifymembers'] == 'email') {
修改前:
- } elseif($_GET['notifymembers'] == 'email') {
- if(!sendmail("{$member['username']} <{$member['email']}>", $subject, $message.$addmsg)) {
- runlog('sendmail', "{$member['email']} sendmail failed.");
- }
- }
复制代码
修改后:
- } elseif($_GET['notifymembers'] == 'email') {
- // 替换消息中的变量占位符
- $user_message = str_replace(
- array('{username}', '{regdate}'),
- array($member['username'], dgmdate($member['regdate'], 'Y-m-d')),
- $message.$addmsg
- );
- // 写入邮件队列,由 Crontab 异步发送
- C::t('common_mail_queue')->insert(array(
- 'uid' => $member['uid'],
- 'toemail' => $member['email'],
- 'username' => $member['username'],
- 'subject' => $subject,
- 'message' => $user_message,
- 'dateline' => TIMESTAMP,
- 'status' => 0,
- ));
- }
复制代码
3、最终实现效果
| 指标 | 优化前 | 优化后 | | 发送方式 | 同步发送,逐封等待 | 写入队列,异步发送 | | 页面阻塞 | 收件人多时卡死 | 秒级完成写入 | | QQ 邮箱限额 | 超过 10 并列连接即拒发 | 每 1 分钟发 1 封,完全合规 | | 失败重试 | 无 | 最多重试 3 次(队列已有机制) | | 变量替换 | 不支持,显示 {username} | 自动替换为实际值 | | 邮箱链接 | 被拼接为 https://域名/mailto:... | 改为纯文本,无解析问题 |
4、重要技术沉淀
需更新到固化摘要:
- 二十七、后台群发邮件队列改造(2026-09-13)
- 功能:后台“用户→发送通知”的邮件发送改为写入队列,异步发送
- 涉及文件:source/admincp/admincp_members.php
- 修改位置:notifymembers() 函数中 email 分支
- 关键机制:复用 common_mail_queue 队列表 + Crontab 异步处理
- 变量替换:{username}、{regdate} 在入队前手动替换
复制代码
踩坑点:
| 踩坑 | 正确做法 | | 直接同步调用 sendmail() | 写入 common_mail_queue,由 Crontab 异步发送 | | QQ 邮箱超过 10 并列连接会拒发 | 队列每 1 分钟发 1 封,规避频率限制 | | 模板变量 {username} 未替换 | 入队前用 str_replace() 手动替换 | | 邮件中 mailto: 链接异常 | 去掉 <html>/<body> 标签,邮箱改为纯文本 |
5、进一步优化的可能性
- 支持更多变量:在 str_replace() 中增加 {email}、{uid}、{credits} 等占位符
- 条件筛选优化:后台筛选条件较多时,可考虑分批写入队列,避免单次请求超时
- 队列进度可视化:在后台增加“队列状态”页面,显示待发数量、失败数量
- 发送时间控制:避免在凌晨等时段发送,可设置队列处理时间窗口
|
|