Estoy usando el siguiente código
const redis = require('redis');
const client = redis.createClient({
url: 'redis://localhost:6379',
retry_strategy: options => new Error('Fail')
});
setInterval(() => client.incr('some-key', console.log), 5000);
corriendo contra un redis local ( docker run --rm -p 6379:6379 redis
)
Después de ver la primera salida, elimino el redis para simular la desconexión. Cuando veo que aparece el error, reinicio el redis. La conexión no vuelve a aparecer. Esperaría que después de descargar el error a los controladores fuera de línea, el cliente intentara volver a conectarse en el siguiente comando. en cambio, permanece en estado cerrado. También regresa con AbortError
lugar de new Error('Fail')
.
A través de la documentación de retry_strategy, parece que node_redis solo intentará volver a conectarse si la función devuelve un número.
Si devuelve un número de esta función, el reintento ocurrirá exactamente después de ese tiempo en milisegundos.
En su caso, está devolviendo un no número, que no cumple con los requisitos para intentar volver a conectarse.
Si devuelve un número que no es, no se volverá a intentar y todos los comandos fuera de línea se vacían con errores.
el flujo que estoy buscando es: al desconectar, vuelva a intentar conectarse 3 veces con un intervalo de 1 segundo. Si en la tercera vez todavía está desconectado, debería devolver todos los comandos pendientes con un error. No quiero que deje de intentar volver a conectarse.
Con una versión modificada del ejemplo dado en README.md, debería poder lograr lo que está buscando. Sin embargo, una vez que se hayan agotado los intentos máximos de 3, el cliente no intentará reconectarse automáticamente en el siguiente comando. Creo que necesitaría llamar manualmente createClient()
nuevamente.
const client = redis.createClient({
url: 'redis://localhost:6379',
retry_strategy: (options) => {
if (options.times_connected >= 3) {
// End reconnecting after a specific number of tries and flush all commands with a individual error
return new Error('Retry attempts exhausted');
}
// reconnect after
return 1000;
}
});
Sí, dejé de lado los intentos ya que eso no hace ninguna diferencia aquí.
Así que básicamente te refieres a:
let client = createClient();
function createClient () {
return redis.createClient({
url: 'redis://localhost:6379',
retry_strategy: (options) => {
client = createClient();
return new Error('Retry attempts exhausted');
}
});
}
¿Lo que implica que ya no puedo pasar mi cliente redis en mi código?
Puede crear un contenedor ligero para administrar una instancia única del cliente redis.
// redisClient.js
let redis = require("redis");
let client = null;
const options = {
url: 'redis://localhost:6379',
retry_strategy: (options) => {
client = null;
return new Error("Redis client connection dropped.");
}
};
module.exports = {
getClient: () => {
if (client == null) {
client = redis.createClient(options);
}
return client;
}
};
Usar:
// usage
let client = require("redisClient.js");
client.getClient().get("some key");
client.getClient().set("some key", "some value");
Supongo que esto es muy similar a llamar a createClient()
en la estrategia de reintento, pero no soy un gran admirador de ese método.
Si desea reducir la necesidad de llamar a getClient()
todo el tiempo, puede hacer un contenedor grande que envuelva todas las llamadas de redis como get()
, set()
, etc., y llame a getClient()
en cada uno de esos métodos. Sin embargo, este método de getClient()
es un método bastante común de administrar conexiones de manera perezosa en lugar de ansiosamente.
claro, pero entonces ¿por qué esta biblioteca proporciona alguna estrategia de reintento si tiene que envolverla en un contenedor de reintento adicional de todos modos?
No soy el autor, así que no estoy seguro de las motivaciones detrás de la implementación específica de retry_strategy. En mis propios usos de node_redis, he encontrado que la funcionalidad actual de retry_strategy es útil, pero estoy de acuerdo en que carece de la capacidad de reintentar automáticamente sin importar qué. En mis usos, siempre he devuelto un número de retry_strategy y nunca devuelvo un error, ya que siempre quiero que se realice el intento de conexión. Sin embargo, registro los errores desde retry_strategy.
¿Qué tal algo como esto? https://github.com/viglucci/node_redis/tree/feature-reconnect-after-flush#retry_strategy -example
var client = redis.createClient({
retry_strategy: function (options) {
if (options.error && options.error.code === 'ECONNREFUSED') {
// End reconnecting on a specific error and flush all commands with a individual error
return new Error('The server refused the connection');
}
if (options.total_retry_time > 1000 * 60 * 60) {
// End reconnecting after a specific timeout and flush all commands with a individual error
return new Error('Retry time exhausted');
}
// attempt reconnect after retry_delay, and flush any pending commands
return {
retry_delay: Math.min(options.attempt * 100, 3000),
error: new Error('Your custom error.');
}
}
});
Conceptualmente tiene mucho sentido. Otra forma de implementar esto podría ser dividiendo esto en dos estrategias:
var client = redis.createClient({
// this strategy handles the pending redis commands when the connection goes down
flush_strategy (options) {
if (options.attempt >= 3) {
// flush all pending commands with this error
return new Error('Redis unavailable')
}
// let the connection come up again on its own
return null;
},
// this strategy handles the reconnect of a failing redis
retry_strategy (options) {
if (options.total_retry_time > 1000 * 60 * 60) {
// The connection is never going to get up again
// kill the client with the error event
return new Error('Retry time exhausted');
}
// attempt reconnect after this delay
return Math.min(options.attempt * 100, 3000)
}
});
Realmente no me gusta el nombre flush_strategy
pero uno podría pensar en algo mejor.
Oh, interesante. Lo que está sugiriendo aquí se parece más a cambiar el nombre del retry_strategy
actual a connection_strategy
y usar el retry_strategy
como lo que ha denominado flush_strategy
.
var client = redis.createClient({
// this strategy handles reconnection
connection_strategy (options) {
if (options.total_retry_time > 1000 * 60 * 60) {
// The connection is never going to get up again
// kill the client with the error event
return new Error('Retry time exhausted');
}
// attempt reconnect after this delay
return Math.min(options.attempt * 100, 3000)
},
// this strategy handles the pending redis commands when the connection goes down
retry_strategy (options) {
if (options.attempt >= 3) {
// flush all pending commands with this error
return new Error('Redis unavailable');
}
// let the client attempt the commands once a connection is available
return null;
},
});
Implementado: https://github.com/viglucci/node_redis/tree/feature-connection-strategy#retry_strategy -example
@Janpot, ¿ puede elaborar el caso de uso en el que sea necesario? Traté de reducir la cantidad de opciones para que sea más fácil de usar y retry_strategy
ya es una opción poderosa.
@viglucci, ¡ gracias por responder aquí y dar buenos comentarios! Personalmente, me gusta su sugerencia de devolver un objeto en caso de que se implemente. Esto no sería realmente un cambio importante, aunque estoy planeando abrir una rama v3 pronto. Actualmente es muy complicado implementar muchas funciones nuevas y eliminar todas las cosas obsoletas ayudará mucho.
@BridgeAR Es más o menos que en este momento, si la conexión de redis se interrumpe por alguna razón, todos mis comandos simplemente se cuelgan. Supongamos que tengo un respaldo para la operación que está haciendo redis, entonces ese respaldo también se bloquea. Prefiero que redis devuelva un error después de algunos reintentos, que puedo detectar y emplear el respaldo. Eso no significa que no quiera que el cliente deje de intentar conectarse. Puede que Redis vuelva a aparecer en un minuto.
Simplemente no veo el sentido del comportamiento actual. Devuelve un error en el controlador de reintentos y luego su cliente muere para siempre. ¿Quién necesita ese tipo de comportamiento a menos que cree un cliente para cada comando?
O puede que no vea el caso de uso para eso, por supuesto 😄
@BridgeAR Gracias. Bueno, si tiene una hoja de ruta "camino a la V3" con tareas simples como eliminar el soporte para los parámetros de configuración / eliminar las advertencias de obsolescencia, etc., le interesará enviar solicitudes de extracción. Probablemente no podría abordar un trabajo grande como agregar soporte para comandos de redis adicionales, pero probablemente podría hacer cosas simples de limpieza de la casa si estuvieran abiertas para PR una vez que esté disponible una rama de función V3.
@viglucci me invité como colaborador. Voy a hacer un plan pronto
+1 para una solución a este problema.
Estoy usando Express, y mientras mi retry_strategy
todavía devuelve enteros para intentar conectarse de nuevo en el futuro, los comandos (y las solicitudes web) continúan acumulándose / respaldando en lugar de lanzar algo para que puedan obtener sigan con sus vidas ... y es de esperar que, eventualmente, se restablezca una conexión antes de que la estrategia diga que se rindan o lo que sea.
¿Se puede lograr esto? @Janpot, ¿ ha encontrado algo disponible actualmente para resolver esta situación?
¿Quizás algo con las opciones enable_offline_queue
y / o retry_unfulfilled_commands
pueda lograr esto?
¡Gracias a todos por su arduo trabajo y ayuda!
Actualización rápida aquí.
Agregué enable_offline_queue = false
a mis opciones createClient
, y parece haber hecho lo que quería, que es:
"Continuar intentando volver a conectarme a mi servidor Redis según la función retry_strategy
, pero arroja errores de inmediato en respuesta a los intentos de usar el cliente mientras tanto. Luego, si puede volver a conectarse, simplemente comience a trabajar de nuevo ".
FYI: Emite el siguiente error en una llamada get
: AbortError: SET can't be processed. Stream not writeable.
Para mi uso como una capa recta entre la base de datos y mi aplicación, esto debería estar bien. Sería bueno saber si existe una solución más "robusta" posible donde:
retry_strategy
Probablemente muchos escenarios difíciles a considerar aquí ...
@nueva casa ,
Gracias por mencionar esto. Queríamos el mismo comportamiento para nuestra aplicación y su solución funcionó para devolver inmediatamente un error a la persona que llama, pero permítanos volver a intentar conectarnos indefinidamente.
Gracias @newhouse , no estoy seguro de haber encontrado esto alguna vez.
En mi opinión, este debería ser el comportamiento predeterminado, o al menos explícitamente mencionado en los documentos alrededor de retry_strategy
. Avíseme si cree que esto último es razonable y abriré un PR para agregar los documentos.
@bwhitty un PR de documentos de cualquier persona sería bienvenido 👍
Un problema con el uso de enable_offline_queue = false
es que los comandos enviados justo después de llamar a createClient
, pero antes de que se abra la conexión, también fallarán.
Un problema con el uso de
enable_offline_queue = false
es que los comandos enviados justo después de llamar acreateClient
, pero antes de que se abra la conexión, también fallarán.
@jkirkwood ¿ Parece que querrá retrasar el intento de enviar sus primeros comandos hasta que se emita el evento ready
(o un evento similar apropiado)?
Comentario más útil
Actualización rápida aquí.
Agregué
enable_offline_queue = false
a mis opcionescreateClient
, y parece haber hecho lo que quería, que es:"Continuar intentando volver a conectarme a mi servidor Redis según la función
retry_strategy
, pero arroja errores de inmediato en respuesta a los intentos de usar el cliente mientras tanto. Luego, si puede volver a conectarse, simplemente comience a trabajar de nuevo ".FYI: Emite el siguiente error en una llamada
get
:AbortError: SET can't be processed. Stream not writeable.
Para mi uso como una capa recta entre la base de datos y mi aplicación, esto debería estar bien. Sería bueno saber si existe una solución más "robusta" posible donde:
retry_strategy
Probablemente muchos escenarios difíciles a considerar aquí ...