Showing posts with label node-redis. Show all posts
Showing posts with label node-redis. Show all posts

Monday, 1 May 2017

Promisifying Redis Client With Bluebird Example (With Pub-Sub Also)

Days before, i was struggling handling callbacks in redis-client. And code was getting more complex, with callbacks.

I have research around, whether is there any thing like promises in redis. And i came to know, that now redis support promises by promisifying node_redis with bluebird.

I have written a simple example code for promisify the node_redis client.

I have written a wrapper around the out of the box node_redis functions (get,set,exist, etc) and that will return you a promise. You can just directly call the wrapper function and can write your all callback code in the "then()" function.

In this example, i am first initializing the redis client and then using the exist function to check, whether the key exist in redis or not, if not then creating the key and publishing it to subscriber, and if the key already exist, then updating the old key value by increment it with 1 and then publishing it to subscribers.

Here is the code for it :

Git hub repository : https://github.com/UtkarshYeolekar/promisify-redis-client

1. redis.js : Which contains all the wrapper functions.

You can remove the logger and can use console.log directly. Also, you can remove the redis-config and can directly specify the port and host in the create Client().


let bluebird = require("bluebird"),
    redis = require('redis'),
    logger = require("./logger"),
    redisConfig = require("./config.js").redisConfig,
    maxReconnectingTry = 4,
    tryReconnecting  = 0,
    // subscriber will pass a callback function and when the redis client
    // will recieve a message, it will call that callback function.
    callback  
bluebird.promisifyAll(redis.RedisClient.prototype);

let redisClient = null;
module.exports = {

        initRedisClient : () =>{
            redisClient =  redis.createClient(redisConfig().port,redisConfig().host)
            logger.debug("Initalizing Redis Client");

            redisClient.on('ready',function() {
            logger.debug(" subs Redis is ready");
            });

            redisClient.on('connect',function(){
                logger.debug('subs connected to redis');
                isRedisConnected = true;
            });

            redisClient.on("message", function(channel, message) {
                logger.info("message recieved on channel :", channel);
                callback(channel,message);
            });

            redisClient.on("error", function (err) {
                logger.debug("Error occurred while connecting to redis " + err);
                isRedisConnected = false;
            });

            redisClient.on('reconnecting',function(err){
                    tryReconnecting++;
                    logger.warn('reconnecting');
                    if(tryReconnecting >= maxReconnectingTry)
                    {
                        logger.error(err);
                        redisClient.quit();
                    }
            });
        },
        getKeyValue: (key) => {
            return redisClient.getAsync(key)
                .then((res, err) => err ? Promise.reject("getKeyValue : "+err) : Promise.resolve(res));
        },
        setKeyValue: (key, value) => {
            return redisClient.setAsync(key, value)
                .then((res, err) => err ? Promise.reject("setkeyvalue : "+ err) : Promise.resolve(res));
        },
        doesKeyExist: key => {
            return redisClient.existsAsync(key)
                .then((res, err) => !res || err ? Promise.resolve(false) : Promise.resolve(res));
        },
        deleteKey: key => {
            return redisClient.delAsync(key)
                .then((res, err) => res ? Promise.resolve(res) : Promise.reject("deleteKey :"+err));
        },
        publishMessage: (channel,message) => redisClient.publish(channel,message),
        endConnection: () => redisClient.quit(),
        subscribeChannel: (channel,cb) => {
             redisClient.subscribe(channel)
             callback = cb;
        }
    }


2. update-redis.js : By using wrapper functions update the redis key and publish the message to the channel.

In this code :
1. I am checking whether the key exist in the redis.
2. if key doesn't exist than create the key and publish the message to the channel(subscriber).
3. if key exist, then get the old value of the key and increment it by 1 and publish it to the channel(subscriber).

I am using "winston" for logging. So, also in this code, you can remove the logger and can use console.log().

let redis = require("./redis.js"),
    logger = require("./logger.js"),
    _baseVersion = 1,
    _currentVersion , _deploymentVersion , _previousDeployedVersion = null;

const versionLabel = "v";
const key = "_deploymentVersion";
const channel = "deployment";


/*redis().deleteKey(key)
.then((res) => redis().doesKeyExist(key))*/
module.exports = {
    updateRedis: () => {
    redis.initRedisClient();
      return redis.doesKeyExist(key)
        .then((res) => res ? redis.getKeyValue(key) : null)
        .then((res) => {
            if (res != null) {
                logger.info("Current Deployed Version", res);
                _previousDeployedVersion = res;
                _currentVersion = parseInt(_previousDeployedVersion.split(versionLabel)[1]) + 1;
                _deploymentVersion = versionLabel + _currentVersion;
            }
            else
                _deploymentVersion = versionLabel + _baseVersion;
            redis.setKeyValue(key, _deploymentVersion)
        })
        .then((res) => {
            logger.info("version updated to : ", _deploymentVersion);
            let message = JSON.stringify({ "_deploymentVersion": _deploymentVersion });
            return redis.publishMessage(channel, message);
        }).then((res) => {
            logger.info("message published to channel :", channel);
            redis.endConnection();
            return Promise.resolve("Redis Updated and message published");
        })
        .catch((res) => {
            logger.error("catch block :->", res);
            redis.endConnection();
            return Promise.reject("Error in updating redis",res);
        });

    }
}

3. subsriber.js : Here we will initialize one more client and will subscribe to the above deployment channel. So, whenever the message is published in channel, we will get notified.


let redis = require("./redis.js");

redis.initRedisClient();
redis.subscribeChannel('deployment',(channel,message)=>{
             console.log(message);
});


Lets, require both the JS (update-redis and subscriber into one js file and named it app.js).

//Updating redis key and publishing it to a channel
let redis = require('./update-redis.js');

//subscribing the channel.
let subs = require('./subscriber.js');

redis.updateRedis().then((res)=>{
    console.log("res",res);
})
.catch((err)=>{
    console.log("eree",err);
});


Now, we can directly run the code by typing node .

You can download the full code from here. It contains the logger and the config file also.

Steps :

1. Download the code.
2. npm install
3. Make sure your redis service is up and running.
4. node app.js

Note : Publisher and Subscriber cannot work on the same client, We require two separate clients for that.


Do provide your valuable feedback 😊