Currently I'm using https://github.com/mranney/node_redis as my node redis client.
client.retry_delay is set to 250ms default.
I tried connecting to redis and once connection was successful, I manually stopped the redis server to see whether client.retry_delay works. But I didn't see it working.
The following log messages are logged on ready & end events on redisClients created using createClient:
[2012-03-30 15:13:05.498] [INFO] Development - Node Application is running on port 8090
[2012-03-30 15:13:08.507] [INFO] Development - Connection Successfully Established to '127.0.0.1' '6379'
[2012-03-30 15:16:33.886] [FATAL] Development - Connection Terminated to '127.0.0.1' '6379'
I didn't see Success message again [ready event was not fired] when the server came back live.
Am I missing something? When will be the retry constant used? Is there a work around to find whether a redis server has come up after a failure from node?
I can't reproduce this. Could you try this code, stop your redis server, and check the log output?
var client = require('redis').createClient();
client.on('connect' , log('connect'));
client.on('ready' , log('ready'));
client.on('reconnecting', log('reconnecting'));
client.on('error' , log('error'));
client.on('end' , log('end'));
function log(type) {
return function() {
console.log(type, arguments);
}
}
Answer # Feb-2020
const redis = require('redis');
const log = (type, fn) => fn ? () => {
console.log(`connection ${type}`);
} : console.log(`connection ${type}`);
// Option 1: One connection is enough per application
const client = redis.createClient('6379', "localhost", {
retry_strategy: (options) => {
const {error, total_retry_time, attempt} = options;
if (error && error.code === "ECONNREFUSED") {
log(error.code); // take actions or throw exception
}
if (total_retry_time > 1000 * 15) { //in ms i.e. 15 sec
log('Retry time exhausted'); // take actions or throw exception
}
if (options.attempt > 10) {
log('10 attempts done'); // take actions or throw exception
}
console.log("Attempting connection");
// reconnect after
return Math.min(options.attempt * 100, 3000); //in ms
},
});
client.on('connect', log('connect', true));
client.on('ready', log('ready', true));
client.on('reconnecting', log('reconnecting', true));
client.on('error', log('error', true));
client.on('end', log('end', true));
For complete running example clone node-cheat and run node connect-retry.js.
Adding to the answer above. Small change. The callback provided should be a method name and not execute the method itself. Something like below:
function redisCallbackHandler(message){
console.log("Redis:"+ message);
}
var redis = require("redis");
var redisclient = redis.createClient();
redisclient.on('connect', redisCallbackHandler);
redisclient.on('ready', redisCallbackHandler);
redisclient.on('reconnecting', redisCallbackHandler);
redisclient.on('error', redisCallbackHandler);
redisclient.on('end', redisCallbackHandler);
Related
I am trying to implement a reconnect mechanism when the connection fails to the rabbitmq queue server.This code is only for consuming messages, Below is my code ( the channel Init function takes care of initialising the consumer and binding to the queue ).
connect() {
let conn = amqp.connect(queueConfig.QUEUE_SERVER_URL + "?heartbeat=60");
return conn;
}
createConnection(){
console.log("Trying to connect amqp");
let self = this;
self.connection = this.connect()
.then(function(connection){
console.log("[AMQP] connected");
connection.on("error",function(err){
if (err.message !== "Connection closing") {
console.error("[AMQP] conn error", err.message);
}
});
connection.on("close", function() {
console.error("[AMQP] reconnecting");
return setTimeout(createConnection, 1000);
});
return connection.createConfirmChannel();
})
.then(self.channelInit);
}
On connection failure I am successfully getting the prompt "[AMQP] reconnecting", but after that queue is not getting reconnected, no other prompts are coming in console log.
Please help.
You have a typo in your method. You need use something like setTimeout(createConnection, 1000); instead of your setTimeout(createConnection(), 1000);
I'm running a cluster of 2 RabbitMQ servers (could be any number) and I have implemented a failover where my app loops the list of RabbitMQs and tries to reconnect when a connection drops.
If the RabbitMQ instance is down which I'm trying to connect to, it takes about 60 seconds to timeout before trying to the next one, which is a very long time. Is there a way to configure the timeout or some other way to make it fail faster. This is causing an unnecessary long downtime. The heartbeat takes care of detecting a failure on an existing connection, but the problem is the initial connect attempt.
Here is my code used for connecting:
connect(callback) {
const self = this;
amqp.connect(rabbitInstances[rabbitInstance] + "?heartbeat=10").then(conn => {
conn.on("error", function(err) {
setTimeout(() => self.reconnect(callback), 5000));
return;
});
conn.on("close", function() {
setTimeout(() => self.reconnect(callback), 5000));
return;
});
connection = conn;
whenConnected(callback);
})
.catch(err => {
setTimeout(() => self.reconnect(callback), 5000));
});
}
reconnect(callback) {
this.rabbitInstance === (rabbitInstances.length - 1) ? this.rabbitInstance = 0 : this.rabbitInstance++;
this.connect(callback)
}
I read the source code for amqplib and saw the second argument to connect accepts an object that contains ordinary socket options. I used that to impose and verify a 2-second timeout as follows:
const amqp = require('amqplib');
const connection = await amqp.connect('amqp://localhost', {
timeout: 2000,
servername: 'localhost',
});
I am using version 0.5.3 of amqplib. The Github URL is here: https://github.com/squaremo/amqp.node.
I want to know form a stand alone Node application if a remote server (running with Socket.io) is up and listening for incoming connections.
If the server is up and listening, then connect with socket-io.client, and if not, record something to a DB.
I don't know how to accomplish this with socket-io.client. The address has IP and Port, so I just can't ping to the IP without the port.
Any ideas? Thanks!
You can just attempt to make a socket.io connection to the server. If it succeeds, then it is listening. If it fails, then apparently it is not listening. Here's a way to do that:
// check a socket.io connection on another server from a node.js server
// can also by used from browser client by removing the require()
// pass hostname and port in URL form
// if no port, then default is 80 for http and 447 for https
// 2nd argument timeout is optional, defaults to 5 seconds
var io = require('socket.io-client');
function checkSocketIoConnect(url, timeout) {
return new Promise(function(resolve, reject) {
var errAlready = false;
timeout = timeout || 5000;
var socket = io(url, {reconnection: false, timeout: timeout});
// success
socket.on("connect", function() {
clearTimeout(timer);
resolve();
socket.close();
});
// set our own timeout in case the socket ends some other way than what we are listening for
var timer = setTimeout(function() {
timer = null;
error("local timeout");
}, timeout);
// common error handler
function error(data) {
if (timer) {
clearTimeout(timer);
timer = null;
}
if (!errAlready) {
errAlready = true;
reject(data);
socket.disconnect();
}
}
// errors
socket.on("connect_error", error);
socket.on("connect_timeout", error);
socket.on("error", error);
socket.on("disconnect", error);
});
}
checkSocketIoConnect("http://192.168.1.10:8080").then(function() {
// succeeded here
}, function(reason) {
// failed here
});
I want my application (lets say a simple node file for now) to work as it is even if redis is not available. I'm not able to do it the correct way. This is what I've tried.
var redis = require('redis');
var redisClient = null;
var getRedisClient = function(){
if(redisClient){
return redisClient;
}
try {
redisClient = redis.createClient({connect_timeout : 5000, max_attempts : 1});
redisClient.on("error", function(err) {
console.error("Error connecting to redis", err);
redisClient = null;
});
return redisClient;
} catch(ex){
console.log("error initialising redis client " + ex);
return null;
}
};
try {
var client = getRedisClient();
console.log("done!");
} catch (ex){
console.log("Exception");
}
However, with this code my application exits if redis is not available (it shouldn't because i've not given a process.exit() command).
How can I solve this?
Checking for Successful Connection on Start
Using a promise, you could guarantee that at least initially, you were able to connect to redis without error within a specified time period:
const redis = require('redis');
const Promise = require('bluebird');
function getRedisClient(timeoutMs){
return new Promise((resolve, reject) => {
const redisClient = redis.createClient();
const timer = setTimeout(() => reject('timeout'), timeoutMs);
redisClient.on("ready", () => {
clearTimeout(timer);
resolve(redisClient);
});
redisClient.on("error", (err) => {
clearTimeout(timer);
reject(err);
});
});
};
const redisReadyTimeoutMs = 10000;
getRedisClient(redisReadyTimeoutMs)
.then(redisClient => {
// the client has connected to redis sucessfully
return doSomethingUseful();
}, error => {
console.log("Unable to connect to redis", error);
});
You Need Proper Error Handling
The redis client being non-null does NOT guarantee using it won't throw an error.
you could experience infrastructure misfortune e.g. crashed redis process, out of memory or network being down.
a bug in your code could cause an error e.g. invalid or missing arguments to a redis command.
You should be handling redis client errors as a matter of course.
DON'T null the Redis Client on Error
It won't give you much but it will force you to check for null every time you try and use it.
The redis client also has inbuilt reconnect and retry mechanisms that you'll miss out on if you null it after the first error. See the redis package docs, look for retry_strategy.
DO Wrap your redis client code with try .. catch ... or use .catch in your promise chain.
DO Make use of a retry_strategy.
I have simple example:
var redis = require('redis'),
client = redis.createClient();
var test = function() {
client.brpop('log', 0, function(err, reply) {
if (err != null ) {
console.log(err);
} else {
.... parse log string ....
}
test();
});
}
test();
How to reconnect redis connection after restart redis server ?
The Redis client automatically reconnects. Just make sure you handle the "error" event from the client. As per the example:
var redis = require('redis');
client = redis.createClient();
client.on('error', function(err){
console.error('Redis error:', err);
});
Otherwise, this code is where the process begins.
this.emit("error", new Error(message));
// "error" events get turned into exceptions if they aren't listened for. If the user handled this error
// then we should try to reconnect.
this.connection_gone("error");
Next, the .connection_gone() method runs on the client.
Notice that you can also listen to a "reconnecting" event to be notified when this happens.