为了账号安全,请及时绑定邮箱和手机立即绑定

慢邮:Nodejs定时任务、邮件发送那点事

国庆长假马上就要结束了,可能有些同学都快把腰玩坏了,有些同学也宅出了新境界,那么面对即将到来的双十一,是否做好了继续“剁手”的准备,或者勇敢的向前一小步,解放双手;好吧,其实我只是想说:假期,你别走!

放假前,激情澎湃的定了一堆计划,要去哪哪好好逛逛,怎么怎么任性一把,要学学Angular2、巩固巩固React、瞄一瞄Vue,耍一耍ES6(看看我这对学习难以抑制的热情!);现在看来,只有梦想最美,现实里早已现原形——“行动的矮子”;于是我给未来的自己发了一封邮件:“珍惜现在吧,未来会更糟!”;当然,我只是个反面例子,希望大家都能做个语言的巨人,行动的巨人并且珍惜现在,勇敢向前!

慢邮:给未来的自己!

慢邮,说白了就是一种定时邮件服务;其实这玩意儿本身并不高大上,只有很少的不太火的公司在做这个,个人只是感觉其中有些情怀还是值得拥有的,毕竟人总会有心累的时候,这时你可能需要一碗“鸡汤”或是喝点“鸡血”,当然也不排除其他刺激手段;慢邮就是基于当前的所见所感,对未来的自己或Ta人说的一些话,比如,此时此刻,你想呤诗一首,或者瞎YY,那又有何不可呢?假如你要写给未来的自己,那么未来的你看到这些话该作何感想呢?惊喜、激情、平淡、给力还是呵呵呢?引用一句歌词:

我不放弃,爱的勇气;我不怀疑,会有真心;我要握住一个最美的梦,给未来的自己!

定时任务:node-schedule

1、指定一个具体时间执行任务

var schedule = require('node-schedule');
var date = new Date(2016, 10, 11, 23, 59, 59);
//月份从0开始算
var j = schedule.scheduleJob(date, function(){
  console.log('这不应该是个虐狗的时节!');
});

2、每天指定一个时间将到期的邮件发送

var registerTask=function(hour,minute,taskname){
    return new Promise(function(resolve,reject){
        var rule = new schedule.RecurrenceRule();
        //每天这个时刻定时执行任务
      rule.dayOfWeek = [0, new schedule.Range(1, 6)];
      rule.hour = hour;
      rule.minute = minute;
      var j = schedule.scheduleJob(rule, function(){
        console.log("开始发送:"+taskname);
            resolve();
      });
    });
}

3、时间的设置取决于rule的配置

(1)、每秒:rule.second=[0,1,2,...59];

(2)、每分钟:rule.minute=[0,1,2,...59];

(3)、每小时:rule.hour=[0,1,2,...23];

(4)、每周几:rule.dayOfWeek=[0,1,...7];

(5)、每月几号:rule.dayOfMonth=[1,2,3,...31];

(6)、每月:rule.month=[1,2,...12];

4、到期重新赋值

var schedule = require('node-schedule');
var date = new Date(2016, 10, 11, 23, 59, 59);
var x = 'old value!';
var j = schedule.scheduleJob(date, function(y){
  console.log(y);
}.bind(null,x));
x = 'new value';

不出意外的话,定时任务就可以跑起来了;

如果你也在使用pm2的话,想必会根据CPU的核数开启多个进程(比如:pm2 start ./app.js -i 4),那么恭喜你,你将碰到一个很有意思的问题:你的定时任务会重复4次发送同一个邮件;

听人说,定时任务最好独立出去单独运行,这个独立站点只执行定时任务,不使用pm2启动,想了想挺有道理的;好笑的是,我独立一个站点不开启多个进程依然会多次发送邮件;因为我使用的是mongoose,这个独立站点里require不到主站里的models,于是我在主站里新增了两个GET请求,发送邮件的站点定时去请求主站的待发送邮件的数据,发送完毕后回调里再请求去更新发送状态,最后的结果是仍然会重复发送;思来想去,然后我就此卡住了!

时间过去了大概不到一个月,突然有一天,我发现作为一个前端,要想学好Node,数据库才是最大的短板啊!再看了下mongodb,果然,不用mongoose,不require主站的models也能在这两个不同的站点访问同一个数据库;于是之前卡住的问题就迎刃而解了;————跳到邮件发送!

邮件发送:nodemailer

这个问题挺多的,但是稳住了就不会有什么问题了;一般就密码的问题,重复发送会被认为是垃圾邮件;以下为完整版,目前为止能顺利的用,倾情推荐:

var emailManage = {};
var nodemailer = require("nodemailer");
var smtpTransport = require('nodemailer-smtp-transport');

emailManage.send = function (_data,geter,title,htmlbody) {
    return new Promise(function(resolve,reject){
        var transport = nodemailer.createTransport(smtpTransport({
            host: "smtp.qq.com", // 主机
            port: 465, // SMTP 端口
            auth: {
                user: "915905174@qq.com", // 账号
                pass: "去邮箱设置后获取,不是QQ密码"
            }
        }));
        transport.sendMail({
            from: "915905174@qq.com", // 发件地址
            to: geter, // 收件列表
            subject: title, // 标题
            html: htmlbody
        }, function(error, response) {
            error&&reject(error);
            resolve(_data);
            //transport.close(); // 如果没用,关闭连接池
        });
    });
}
module.exports=emailManage;

跳回定时任务。独立出来的站点是可以连接到主站的数据库的,不用想着去require主站的models了;至此,慢邮版块的定时邮件发送任务就完成了;

var  mongodb = require('mongodb');
var  server  = new mongodb.Server('localhost', 27017, {auto_reconnect:true});
var  db = new mongodb.Db('dbname', server, {safe:true});
var dbColl=null;
var openDB=function(){
    return new Promise(function(resolve,reject){
        db.open(function(err,dab){
            err&&reject(err);
            resolve(dab);
        });
    });
}
var getCollection=function(db,coll){
    return new Promise(function(resolve,reject){
        db.collection(coll,{safe:true},function(err,collection){
            err&&reject(err);
            dbColl=collection;
            resolve(collection);
        });
    });
}
var getFus=function(collection){
    return new Promise(function(resolve,reject){
        var dateDayStamp = new Date(new Date().getFullYear()+'-'+(new Date().getMonth()+1)+'-'+new Date().getDate()).getTime()+'';
        collection.find({timeStamp:{$lte:dateDayStamp},sended:0}).toArray(function(err,fus){
            err&&reject(err);
            resolve(fus);
        });
    });
}
var fusPromises=function(fus){
    if (fus.length) {
        return fus.map(function(f){
            return emailManage.send(
                {
                    fid:f.fid,
                    getter:f.getter,
                    sender:f.sender,
                    emailto:f.emailto
                },
                f.emailto,
                '你好,这是一封'+f.timeName+'前'+f.sender+'发给您的-时光慢递',
                decodeURIComponent(f.body)
            ); 
        });
    }
    return [Promise.reject('need not to send ...')];
    //直接跳到catch
}
var updateFus=function(ifs){
    var updateOne=function(f){
        return new Promise(function(resolve,reject){
            dbColl.update({fid:f.fid},{$set:{sended:1}},{},function(err){
                err&&reject(err);
                console.log('sended to '+f.emailto);
                console.log('+---------------------------------+');
                resolve();
              });
        });
    }
    return ifs.map(function(f){
        return updateOne(f);
    });
}
registerTask(22,45,'慢邮')
.then(function(){
    return openDB();
})
.then(function(dba){
    return getCollection(dba,'futures');
})
.then(function(collection){
    return getFus(collection);
})
.then(function(fus){
    return Promise.all(fusPromises(fus));
})
.then(function(ifs){
    return Promise.all(updateFus(ifs));
})
.then(function(){
    console.log('all sended');
})
.catch(function(err){
    console.log(err);
});

好了,先给Promise点个赞!

原文来自:花满楼(http://www.famanoder.com/bokes/57f6cb7c25beee9890c8985c);

点击查看更多内容
13人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消