使用wp_mail与wp cron后台发批量邮件遇问题求助
WordPress批量邮件与WP Cron问题排查与修复
问题描述
尝试通过wp_mail和WP Cron后台发送批量邮件,代码可正常发送邮件,但存在两个核心问题:
- 当邮箱数组包含900+条数据时,页面会持续加载直至任务完成,无法先返回成功提示再在后台执行,不符合WP Cron后台运行的预期。
- 使用
wp_schedule_single_event创建的定时任务,无法在Advanced Cron Manager插件的Scheduled Actions中查看,看似函数已执行,但定时任务并未正确创建。
原始代码:
set_time_limit(0); function send_newsletter() { $i = 0; foreach ($emails as $email) { $i++; if ($i < count($emails)) { sleep(60); wp_mail($email, $subject, $msg, $headers); continue; }else{ break; } } } $args = array( false ); if (!wp_next_scheduled('subscribers_newsletter')) { wp_schedule_single_event( time() + 10, 'subscribers_newsletter', $args ); add_action( 'subscribers_newsletter', 'send_newsletter' ); do_action( 'subscribers_newsletter' ); }else{ echo "Wait until the previous job finished"; }
问题1:页面持续加载,未后台执行
原因
代码中直接调用了do_action('subscribers_newsletter'),这会立即同步执行任务函数,而非交给WP Cron在后台触发。WP Cron本质是伪定时任务,依赖后续站点访问触发任务,手动触发action会让任务在当前请求内同步跑完,导致页面卡死。此外,单次循环900+邮件并加上sleep(60),会远超服务器和WP Cron的超时限制,进一步加剧页面阻塞。
解决方案
- 移除
do_action('subscribers_newsletter')调用,仅保留任务调度逻辑,让WP Cron在指定时间自动触发。 - 拆分任务:每次只发送小批量邮件(比如10封),完成后调度下一次任务,直到所有邮件发送完毕,避免单次执行时间过长。
问题2:定时任务在插件中无法查看
原因
add_action放在了调度条件判断内部,仅第一次调度时才绑定钩子,后续无法正确触发;且钩子绑定应在WP初始化阶段完成,而非请求时临时绑定。- 手动调用
do_action会直接执行任务,导致调度的任务被标记为已完成,插件中自然无法看到。 - 传递的
$args参数未被函数接收,虽不致命,但可能导致钩子匹配异常。
解决方案
- 将
add_action放在全局位置(比如主题functions.php或插件主文件开头),确保WP初始化时完成钩子绑定。 - 移除
do_action调用,让任务完全由WP Cron调度触发。 - 按需传递参数,若无需参数可省略
$args。
修正后的代码示例
// 全局绑定钩子,确保WP初始化时加载 add_action('subscribers_newsletter', 'send_newsletter_batch'); // 分批发送邮件的核心函数 function send_newsletter_batch() { // 从WP选项读取待发送数据(避免变量作用域问题) $pending_emails = get_option('newsletter_pending_emails', []); $subject = get_option('newsletter_subject', ''); $msg = get_option('newsletter_message', ''); $headers = get_option('newsletter_headers', ['Content-Type: text/html; charset=UTF-8']); if (empty($pending_emails)) { // 无待发邮件,清理存储的内容 delete_option('newsletter_pending_emails'); delete_option('newsletter_subject'); delete_option('newsletter_message'); delete_option('newsletter_headers'); return; } // 单次发送10封,可根据服务器配置调整 $batch_size = 10; $current_batch = array_slice($pending_emails, 0, $batch_size); $remaining_emails = array_slice($pending_emails, $batch_size); // 发送当前批次邮件 foreach ($current_batch as $email) { wp_mail($email, $subject, $msg, $headers); sleep(2); // 短间隔避免触发邮件服务商限制 } // 若有剩余邮件,调度下一次任务(5分钟后执行) if (!empty($remaining_emails)) { update_option('newsletter_pending_emails', $remaining_emails); if (!wp_next_scheduled('subscribers_newsletter')) { wp_schedule_single_event(time() + 300, 'subscribers_newsletter'); } } } // 触发任务的入口(可放在后台页面或短代码中) function trigger_newsletter() { // 替换为你的900+邮箱数组、主题、内容 $all_emails = ['email1@example.com', 'email2@example.com', ...]; $subject = '订阅更新通知'; $msg = '这是你的批量邮件内容'; // 检查是否已有任务在运行 if (wp_next_scheduled('subscribers_newsletter')) { echo '已有邮件发送任务在进行,请等待完成'; return; } // 将待发送数据存储到WP选项 update_option('newsletter_pending_emails', $all_emails); update_option('newsletter_subject', $subject); update_option('newsletter_message', $msg); // 调度第一次任务(10秒后执行) wp_schedule_single_event(time() + 10, 'subscribers_newsletter'); echo '邮件发送任务已启动,将在后台分批发送'; }
额外提示
- 若站点访问量低,建议配置服务器真实CronJob触发
wp-cron.php,确保任务按时执行(避免依赖站点访问触发的延迟)。 - 调整
$batch_size和调度间隔时,需参考服务器超时设置和邮件服务商的发送限制,避免被封禁。
内容的提问来源于stack exchange,提问作者Pixie
相关产品推荐
相关产品推荐

