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

Node.js比特币价格告警应用故障排查:未触发邮件发送求助

比特币价格告警邮件未发送问题排查与修复

以下是针对你代码中可能导致邮件未发送的问题分析及修复方案:

1. 异步邮件发送未正确等待

sendEmail 大概率是异步函数(比如调用邮件API通常返回Promise),但你在队列处理器中直接调用却没有用await,导致代码会在邮件发送完成前就执行done(),同时response会是一个Promise对象,而非包含error属性的结果,错误判断逻辑完全失效。

修复代码:

alertQueue.process(async function(job, done) {
    const {mailTo, title, text} = job.data;

    const mailObj = {
        from: process.env.SendGridForm,
        recipients: mailTo,
        subject: title,
        message: text
    }

    try {
        await sendEmail(mailObj);
        done();
    } catch (err) {
        done(new Error(`Error Sending Alert: ${err.message}`));
    }
})

2. Alert保存操作未处理异步

在forEach循环中调用alert.save()但没有用await,这会导致:

  • 数据库保存操作可能还未完成,下一次Cron任务运行时,该Alert的状态还是Created,会重复触发告警
  • 保存失败的错误无法被捕获,无法排查问题

修复方案:把forEach换成for...of循环,支持异步等待:

// 替换原有的alerts.forEach(...)
for (const alert of alerts) {
    if(alert.price <= price) {
        const mailTo = alert.email;
        const title = `Bitcoin is UP!`;
        const text = `Price of Bitcoin has just exceeded your alert price of ${alert.price} USD. Current price is ${price} USD.`;

        await alertQueue.add(
            {mailTo, title, text},
            {
                attempts: 3,
                backoff: 3000
            }
        )

        alert.status = "Triggered";
        await alert.save();
    }
}

3. 未声明变量导致的潜在问题

mailTo、title、text在循环中直接赋值,没有用let/const声明,会成为全局变量,在并发处理多个Alert时可能出现变量值被覆盖的情况。

修复:如上一段代码所示,给这些变量添加const声明。

4. 缺少队列错误监听

Bull队列如果连接Redis失败或者出现其他内部错误,不会主动抛出到控制台,导致你无法察觉队列是否正常运行。

添加错误监听:

// 创建队列后添加以下代码
alertQueue.on('error', (err) => {
    console.error('Alert Queue Error:', err);
});

alertQueue.on('failed', (job, err) => {
    console.error(`Job ${job.id} failed:`, err.message);
});

5. 日志缺失导致无法定位问题

没有添加关键步骤的日志,无法确认:

  • Cron任务是否按时触发
  • 是否正确获取到比特币价格
  • 是否找到符合条件的Alert
  • 队列是否成功添加任务

添加日志示例:

let sendAlert = new CronJob("*/25 * * * * *", async function () {
    console.log('Cron job triggered at:', new Date());
    
    let priceObj = await currentPrice();
    if (priceObj.error) {
        console.error('Failed to get Bitcoin price:', priceObj.error);
        return;
    }

    let price = priceObj.data;
    console.log('Current Bitcoin price:', price);
    
    const alerts = await Alert.find({status: "Created"});
    console.log(`Found ${alerts.length} active alerts`);

    for (const alert of alerts) {
        if(alert.price <= price) {
            console.log(`Triggering alert for ${alert.email} (threshold: ${alert.price})`);
            // ... 后续代码
        }
    }
});

额外检查点

  • 确认.env文件中的RedisURL、SendGridForm等配置项是否正确
  • 验证sendEmail函数本身是否正常工作,可以单独写测试代码调用该函数发送邮件
  • 检查Redis服务是否正常运行,队列是否能正常连接Redis

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 12:30:42