Friday, January 26, 2018

how to create and manage mail queue/demon/cron in nodejs

From web application, we can send mail immediately when needed like user registration, but at sometimes we have to send mail after sometimes.

If you need to send mail to group of users like send mail to all users who are available in group, at that cases we can not send to all users immediate because they may be more than 100 to 10000.if we send email to them immediate then our server will down and user have to wait until all messages not send. To overcome this problem we should make mail queue/demon/cron in nodejs.

Before start reading mail queue, review following link
https://laxmanchavda.blogspot.in/2018/01/dynamic-email-template-in-nodejs.html

from above post, we had discussed how to create a dynamic email template, and manage dynamic data in this template and then send mail immediate.

Now, to make mail queue / mail demon / mail cron, create "emailTemplate.js" model.

=>emailTemplate model and schema structure

emailTemplate.js
/*
 * @Purpose : Store Email messages
 */
'use strict';
/*
 *Declare variables and include mongoose
 */
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
/*
 *Define structure of collection
 */
var emailQueueSchema = new Schema({
    send_to: {
        type: String, //for user email address to whom send mail
        required: true
    },
    subject: {
        type: String, //for email subject
        required: true
    },
    message: {
        type: String, //for email dynamic message body without header and and footer
        required: true
    },
    error_status: {
        type: Number,
        default: 0 // status of error occurs if more than 5 time then ignore it.
    },
    priority: {
        type: Number,
        default: 0  //0=>not immediate, 1=> immediate
    },
    created_at: {
        type: Date,
        default: Date.now
    }
}, {collection: 'email_queue'});

/*
 *Define model and export it for use in other page
 */
var EmailQueue = mongoose.model("emailQueue", emailQueueSchema);
module.exports = EmailQueue;

now, we will save email message in "email_queue" collection instead of send mail and  then will send message via mail queue / mail demon / mail cron.

after making this and saving all emails in this collection we will create simple new nodejs web app / cron job which will run on 1010 port. you can use any port which is greater than 1000.

EX: mailCronServer.js
/*
 * @Purpose : mail queue / mail demon
 */
"use strict";
var express = require("express");
var https = require('https');
var http = require("http");
var fs = require('fs');
var app = express();
var nodemailer = require('nodemailer');
var smtpTransport = require('nodemailer-smtp-transport');
var EmailQueue = require("./app/data/models/emailQueue");

var httpServer = http.createServer(app).listen(1010, function(req, res) {
    //Starts for send mail queue on server start or restart
    console.log('Email server is listening on port 1010...');
 
    //Use this variable for store email template
    var emailTemplatesHtml;

    //This function is used to call in recursively.
    var emailQueueRecursive = function() {
        EmailQueue.findOne({error_status: {$lt: 5}},'',{sort: '-priority'},function(err, emailQueue) {
            if (err) {
                //on error recall again and again.
                setTimeout(emailQueueRecursive, 2000);
            } else {
                //check is emailQueue record is available of not
                if (!!emailQueue) {

                    var emailTemplatesHtmlTemp = emailTemplatesHtml;

                    var message = emailTemplatesHtmlTemp.replace(/##DYNAMIC_EMAIL_HTML##/g, emailQueue.message);

                    // create transporter object using the default SMTP transport
                    var transporter = nodemailer.createTransport(smtpTransport({
                        host: "smtp host",
                        secure: true,
                        auth: {
                            user: "smtp email id",
                            pass: "smtp password"
                        },
                        tls: {
                            rejectUnauthorized: false
                        }
                    }));

                    transporter.sendMail({
                        from: "send from email id",
                        to: emailQueue.send_to,
                        subject: emailQueue.subject,
                        message: message,
                    }, function(error, response) {
                        if (error) {
                            console.log("Mail error: ");
                            console.log(error);
                            //on error, increment error_status and recall again and again.
                            emailQueue.set("error_status", parseInt(emailQueue.error_status) + 1);
                            emailQueue.save(function(err, emailQueueRes) {
                                emailQueueRecursive();
                            });
                        } else {
                            //on mail sent, Remove current mail queue record and recall for next recored.
                            emailQueue.remove(function(err, emailQueueRes) {
                                emailQueueRecursive();
                            });
                        }
                    });
                } else {
                    //if record not found , recall after some time for continue check.
                    setTimeout(emailQueueRecursive, 2000);
                }
            }
        });
    };

    //Get email template from here for get it only first time and use it life time.
    emailTemplatesHtml = fs.readFileSync(path.join("emailTemplates.html")).toString(); // here pass your application full path like (/var/www/html/...)
    //call this function for start mail queue, This call first at server start or restart.
    emailQueueRecursive();
});

from the above demo "emailQueueRecursive" function will call recursively after 2seconds (2000 milliseconds), you can change as you want.

If any mail sending fails, then again it will try to send until 5 times. after 5 times this mail fails then ignore this mail for a lifetime and continue on another pending mail.

Now, after creating this mail queue, you have two node server one for your application server and second for mailCronServer.

You can start this all servers by using node or pm2 module or any other module.

No comments:

Post a Comment