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

控制器内Fire and Forget Worker实现:长查询异步邮件通知方案问询

解决长耗时查询的Fire-and-Forget方案(Node.js+Express+Postgres)

嘿,这个场景我太熟悉了!云服务的超时限制真的头疼,不过用异步任务队列就能完美解决你的问题。核心思路就是把耗时的查询和邮件发送从HTTP请求的上下文里剥离出来,让请求一进来就返回200,剩下的脏活交给后台的工作进程去做。

下面是具体的实现步骤和代码修改:

第一步:安装必要依赖

我们用bullmq来做任务队列(可靠、支持重试和持久化),用nodemailer来发邮件:

npm install bullmq nodemailer

第二步:重构queries.js,把查询任务丢进队列

原来的getReport直接在请求里执行查询,现在改成只接收参数并添加任务到队列,立即返回响应:

const Queue = require('bullmq').Queue;
// 创建一个名为'report-generator'的任务队列,默认依赖Redis(如果没有Redis,也可配置其他持久化存储)
const reportQueue = new Queue('report-generator');

const getReport = (request, response) => {
  const { business_group_id, initial_date, final_date, user_email } = request.body;
  // 注意:需要让用户提供接收结果的邮箱地址
  
  // 将查询参数和用户邮箱打包成任务,加入队列
  reportQueue.add('generate-and-send-report', {
    business_group_id,
    initial_date,
    final_date,
    user_email
  });

  // 立即返回200状态码,告知用户任务已启动
  response.status(200).json({
    message: '报告生成任务已启动,结果将发送到你的邮箱,请耐心等待'
  });
};

module.exports = { getReport };

第三步:创建工作进程worker.js处理队列任务

这个文件会单独运行,负责从队列取出任务、执行查询并发送邮件:

const Worker = require('bullmq').Worker;
const Pool = require('pg').Pool;
const nodemailer = require('nodemailer');

// 1. 初始化Postgres连接池(复用你原有的配置)
const pool = new Pool({
  user: 'xxx',
  host: 'xx.xxx.xx.xxx',
  database: 'xxxxxxxx',
  password: 'xxxxxxxx',
  port: xxxx,
});

// 2. 配置邮件发送器(这里以Gmail为例,可替换为你的企业邮箱服务)
const transporter = nodemailer.createTransport({
  service: 'Gmail',
  auth: {
    user: 'your-service-email@gmail.com',
    pass: 'your-app-specific-password' // Gmail需开启两步验证后使用应用专用密码
  }
});

// 3. 创建工作进程处理队列任务
const worker = new Worker('report-generator', async (job) => {
  const { business_group_id, initial_date, final_date, user_email } = job.data;

  try {
    // 执行耗时的查询操作
    const results = await pool.query(`SELECT GIANT QUERY`, [business_group_id, initial_date, final_date]);
    
    // 格式化查询结果为友好的邮件内容(这里用JSON示例,也可导出为CSV附件)
    const emailContent = `
      <h3>你的报告已生成</h3>
      <p>查询结果详情:</p>
      <pre>${JSON.stringify(results.rows, null, 2)}</pre>
    `;

    // 发送结果邮件
    await transporter.sendMail({
      from: '报告服务 <your-service-email@gmail.com>',
      to: user_email,
      subject: '你的数据报告已完成',
      html: emailContent
    });

    return '任务完成:报告已发送至用户邮箱';
  } catch (error) {
    // 错误处理:给用户发送失败通知
    await transporter.sendMail({
      from: '报告服务 <your-service-email@gmail.com>',
      to: user_email,
      subject: '报告生成失败',
      text: `抱歉,报告生成过程中出现错误:${error.message}`
    });
    throw error; // 抛出错误让BullMQ自动重试(可配置重试次数和间隔)
  }
});

// 监听工作进程日志,方便排查问题
worker.on('completed', (job) => {
  console.log(`任务 ${job.id} 处理完成`);
});

worker.on('failed', (job, err) => {
  console.log(`任务 ${job.id} 处理失败: ${err.message}`);
});

第四步:启动服务和工作进程

现在需要同时启动API服务和后台工作进程:

  • 启动API:node index.js
  • 启动工作进程:node worker.js

生产环境中可以用PM2等进程管理器来托管这两个进程,确保它们持续运行。

方案优势

  • 完全规避超时限制:HTTP请求瞬间返回200,不受云服务2分钟超时约束
  • 任务可靠不丢失:BullMQ将任务持久化到Redis,就算API进程重启,任务也会被工作进程继续处理
  • 可扩展性强:可启动多个工作进程并行处理大量任务,提升整体效率

可选优化点

  • 返回任务ID给用户,支持查询任务状态(BullMQ内置任务状态查询API)
  • 将查询结果导出为CSV附件发送,提升用户体验
  • 自定义BullMQ的重试策略(如失败后5分钟重试,最多重试3次)

内容的提问来源于stack exchange,提问作者Matheus Lacerda

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:07:30