You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何提前返回JSON成功消息并后台持续执行邮件发送任务?

异步批量发送邮件:立即返回响应,后台持续执行

问题根源

你的代码虽然先输出了JSON响应,但PHP默认的输出缓冲机制会把响应内容暂存,直到整个脚本执行完成(所有邮件发送完毕)才发送给浏览器。同时,若用户关闭页面,HTTP连接断开后,PHP可能终止脚本,导致未发送的邮件中断。


解决方案步骤

1. 立即发送响应并断开HTTP连接

修改响应输出部分,确保浏览器立刻收到JSON,同时让脚本在后台继续执行:

// 清空所有输出缓冲,避免响应被滞留
while (ob_get_level()) {
    ob_end_clean();
}

// 设置响应头
header('Content-Type: application/json');
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');

// 输出成功响应
echo json_encode(["status" => "success"]);

// 强制将输出发送到浏览器
flush();
ob_flush();

// 核心设置:即使客户端断开连接,脚本继续执行
ignore_user_abort(true);
// 取消脚本执行时间限制(根据实际发送量调整,0表示无限制)
set_time_limit(0);

// 断开与客户端的连接(不同环境适配)
if (function_exists('fastcgi_finish_request')) {
    // 推荐:PHP-FPM/Nginx环境下使用此函数,直接结束HTTP请求
    fastcgi_finish_request();
} else {
    // Apache/其他环境:通过关闭连接头实现
    header('Connection: Close');
}

2. 修正邮件发送逻辑(修复原代码的计数bug)

原代码中$total_sent的计数逻辑存在错误,会导致多发送一封邮件,同时优化等待逻辑:

$mail = new PHPMailer(true);
try {
    $mail->isSMTP();
    $mail->Host = 'smtp.example.net';
    $mail->SMTPAuth = true;
    $mail->Username = 'it.example@example.com';
    $mail->Password = 'your_smtp_password'; // 替换为真实SMTP密码
    $mail->SMTPSecure = 'TLS';
    $mail->Port = 587;
    $mail->CharSet = 'utf-8';
    $mail->setFrom('it.example@example.com', 'IT example');

    $limit = 120;
    $total_sent = 0;
    // 去重避免重复发送
    $to_emails = array_unique($to_emails);

    foreach ($to_emails as $email) {
        // 达到发送限制则终止循环
        if ($total_sent >= $limit) {
            break;
        }

        $mail->addAddress($email);
        $mail->Subject = "example";
        $mail->Body = "请填写实际邮件内容"; // 替换为真实邮件内容
        $mail->isHTML(true);

        $mail->send();
        $mail->clearAddresses();

        $total_sent++;

        // 仅在还有未发送邮件且未达限制时等待
        if ($total_sent < count($to_emails) && $total_sent < $limit) {
            sleep(21);
        }
    }
} catch (Exception $e) {
    // 后台脚本无法向浏览器输出错误,需记录到日志文件
    $log_path = '/var/log/mail_send_errors.log'; // 替换为你的日志路径
    file_put_contents(
        $log_path,
        date('Y-m-d H:i:s') . " 发送失败: " . $e->getMessage() . " 邮箱: " . $email . "\n",
        FILE_APPEND
    );
}

3. 重要注意事项

  • 输出缓冲必须清空:如果遗漏这一步,响应仍会被PHP滞留,直到脚本结束。
  • 服务器环境适配:fastcgi_finish_request()是PHP-FPM环境下的最优方案,若使用Apache mod_php,Connection: Close头也能生效,但可靠性稍差。
  • 错误日志必须配置:后台运行的脚本无法向用户反馈错误,一定要将错误信息写入日志文件,方便排查问题。
  • 超时配置检查:确保服务器全局配置(如PHP.ini的max_execution_time、FPM的request_terminate_timeout)允许脚本长时间运行。
  • 邮箱去重:原代码可能从不同分组查询到重复邮箱,添加array_unique($to_emails)避免重复发送。

内容的提问来源于stack exchange,提问作者Emir Karakutuk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 04:14:52