У меня есть приведенный ниже код, и я пытаюсь имитировать разорванное соединение, используя iptables -A OUTPUT -p tcp --dport 6379 -j REJECT
.
self.client = redis.createClient(self.cfg.port, self.cfg.host, {
retry_strategy: function (options) {
console.log('retry strategy check');
console.log(options);
if (options.error) {
if (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.error.code === 'ECONNRESET') {
return new Error('The server reset the connection');
}
if (options.error.code === 'ETIMEDOUT') {
return new Error('The server timeouted 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');
}
if (options.attempt > 5) {
// End reconnecting with built in error
return new Error('Retry attempts ended');
}
// reconnect after
return 1000;
}
});
self.client.on('ready', function () {
log.trace('Redis client: ready');
});
self.client.on('connect', function () {
log.trace('Redis client: connect');
});
self.client.on('reconnecting', function () {
log.trace('Redis client: reconnecting');
});
self.client.on('error', function (err) {
log.error({err: err}, 'Listener.redis.client error: %s', err);
process.exit(1);
});
self.client.on('end', function () {
log.trace('Redis client: end');
});
self.client.on('warning', function () {
log.trace('Redis client: warning');
});
Предполагается, что все ошибки Redis возникают в событии ошибки. Но вот что у меня в выводе консоли:
21: 00: 14.666Z Сценарий TRACE: Клиент Redis: подключение
21: 00: 14.695Z Скрипт TRACE: Клиент Redis: готов
21: 10: 23.837Z Сценарий TRACE: клиент Redis: конец
повторная проверка стратегии
{попытка: 1,
ошибка: {[Ошибка: подключение Redis к redis.callision. info: 6379 failed - read ECONNRESET] код: 'ECONNRESET', номер ошибки: 'ECONNRESET', syscall: 'read'},
total_retry_time: 0,
times_connected: 1}/node_modules/q/q.js:155
бросить е;
^
AbortError: соединение потока завершено и команда прервана. Это могло быть обработано.
в RedisClient.flush_and_error (/node_modules/redis/index.js:350:23)
в RedisClient.connection_gone (/node_modules/redis/index.js:612:18)
в RedisClient.on_error (/node_modules/redis/index.js:398:10)
в Socket.(/node_modules/redis/index.js:272:14)
в emitOne (events.js: 90: 13)
в Socket.emit (events.js: 182: 7)
в emitErrorNT (net.js: 1255: 8)
в nextTickCallbackWith2Args (node.js: 474: 9)
в process._tickCallback (node.js: 388: 17)
И в качестве вопроса: почему обнаружение разрыва связи занимает около 10 минут? Есть ли способ вызвать ошибку в случае отсутствия ответа в течение 10 секунд? Может быть любой вариант, например response_timeout и т. Д.
@pavelsc Я пытался воспроизвести это, но пока не смог.
Пожалуйста, попробуйте воспроизвести проблему без каких-либо сторонних модулей. В настоящее время вы, кажется, хотя бы используете q
.
У меня такая же ошибка. Если я намеренно предоставляю клиенту Redis неверный URL-адрес, метод on.error не вызывается. Вот простой пример:
var redis = require("redis");
exports.handler = function (event, context, callback) {
console.log("Executing test lambda for diagnosing redis issues");
var redisInfo = {
HOST: process.env.REDIS_HOST,
PORT: process.env.REDIS_PORT
};
console.log(process.env.REDIS_HOST);
console.log(process.env.REDIS_PORT);
console.log("Connecting to Redis...");
var client = redis.createClient({
host: redisInfo.HOST,
port: redisInfo.PORT,
retry_strategy: function (options) {
if (options.total_retry_time > 2000) {
console.log("throwing an error...");
return new Error('Retry time exhausted');
}
return 200;
}
});
// if you'd like to select database 3, instead of 0 (default), call
// client.select(3, function() { /* ... */ });
client.on("error", function (err) {
console.log("Error " + err);
callback(null, "Error with Redis");
});
client.on('connect', function() {
console.log("Connected to Redis");
});
client.on('end', function() {
console.log("Redis end");
});
client.set("string key", "string val", redis.print);
client.hset("hash key", "hashtest 1", "some value", redis.print);
client.hset(["hash key", "hashtest 2", "some other value"], redis.print);
client.hkeys("hash key", function (err, replies) {
console.log(replies.length + " replies:");
replies.forEach(function (reply, i) {
console.log(" " + i + ": " + reply);
});
client.quit();
});
client.quit();
callback(null, "Success");
};
На данный момент я возвращаюсь к использованию connect_timeout, которое правильно выдает сообщение об ошибке после истечения времени ожидания подключения.
У меня та же проблема, использование настраиваемой retry_strategy с плохой конечной точкой приводит к ошибке «AbortError:»
Это зацепило меня и сегодня. Если коротко взглянуть на код, это может показаться преднамеренным поведением. https://github.com/NodeRedis/node_redis/blob/79558c524ff783000a6027fb159739770f98b10e/index.js#L405 явно заявляет, что если установлен retry_strategy
, не выдавать ошибку, а вместо этого продолжает ее выдавать. Мне было бы любопытно услышать, почему это так, хотя, похоже, нет причины, по которой он не может испускать его вместо того, чтобы бросать с беглого взгляда. Есть ли причина, по которой это условие не может быть удалено, так что ошибка всегда будет выдаваться?
У меня тоже постоянно возникает эта проблема.
Я тоже не могу отловить ошибки при получении ENOTFOUND
.
{
host: "foo",
retry_strategy: function (options) {
if (options.error && options.error.code === "ENOTFOUND") {
return new Error("The server was not found");
}
// reconnect after
return 1000;
}
с участием:
redis.on("error", err => {
console.error("Cache Error: " + err);
});
Отлаживая приложение, я перехожу к проверке ENOTFOUND
как указано выше в retry_strategy
но она не вызывает обработчик событий ошибки.
У меня такая же проблема. Покопавшись в исходном коде, я обнаружил, что если мы изменим
эта строка (или включить режим отладки)
https://github.com/NodeRedis/node_redis/blob/009479537eb920d2c34045026a55d31febd1edd7/index.js#L381 -L382
И вставьте сюда этот фрагмент кода (сразу добавьте ошибку в массив)
https://github.com/NodeRedis/node_redis/blob/009479537eb920d2c34045026a55d31febd1edd7/index.js#L352 -L353
if (options.error) {
aggregated_errors.push(options.error);
}
Он работает и выдает ошибку правильно.
Вложенный цикл в этой функции не выполняется, потому что command_queue пуста, а ошибка никогда не добавляется в массив и, следовательно, не генерируется. Если я правильно понимаю, это довольно старый фрагмент кода, поэтому нам нужен ввод от сопровождающих или от @BridgeAR
Я также видел, что при первом неудачном соединении генерируется событие «конец», которое может что-то означать (или нет), я взял Redis два дня назад, поэтому еще не уверен, как работают внутренние компоненты. Я постараюсь раскопаться и покопаться еще немного, когда / когда у меня будет время.
И буквально с этой проблемой, похоже, связана следующая проблема # 1198.
@ v1adko Сейчас я путешествую, но постараюсь взглянуть на нее сегодня или завтра (если только Рубен не опередит меня).
У меня намеренно неправильный URL-адрес redis для проверки сценариев ошибок, но я вижу, что моя retry_strategy не вызывается при попытке подключиться к redis. retry_strategy вызывается только при закрытии соединения.
`const redis = require ('redis');
const log = require ('./ logUtil'). logger;
module.exports.connect = () => {
var redisRetryStrategy = function(options) {
if (options.error && options.error.code === 'ECONNREFUSED') {
// End reconnecting on a specific error and flush all commands with
// a individual error
log.error('The redis server refused the connection');
return new Error('The redis server refused the connection');
}
log.info(`Already spent ${options.total_retry_time} milliseconds to re-establish connection with redis`);
if (options.total_retry_time > 2000) {
// End reconnecting after a specific timeout and flush all commands
// with a individual error
log.error('Retry time exhausted');
return new Error('Retry time exhausted');
}
log.info(`Attempting ${options.attempt} time to establish connection with redis`);
if (options.attempt > 5) {
// End reconnecting with built in error
log.error('Exhausted the retry attempts to establish connection to redis');
return undefined;
}
// reconnect after
return 100;
}
log.info(`Redis connection url is :${process.env.REDIS_URL}`);
var redisClient = redis.createClient(qualifyUrl(process.env.REDIS_URL), {
retry_strategy: redisRetryStrategy
});
redisClient.offline_queue_length = 3;
redisClient.on('connect', function() {
console.log('Connected to Redis');
});
redisClient.on('reconnecting', function() {
console.log('Re-Connecting to Redis');
});
redisClient.on('error', (err)=> {
console.log(`Error trying to create redis connection: ${JSON.stringify(err)}`);
});
return redisClient;
}
const qualifyUrl = (url) => {
return '//' + url.replace (/ ^ \ / + /, "");
};
`
Может ли кто-нибудь помочь мне решить эту проблему.
То же самое. Этот неприятный прием, кажется, создает ожидаемое поведение, но не уверен, имеет ли он какое-либо более широкое значение:
const client = redis.createClient({
retry_strategy: ({error}) => client.emit('error', error)
});
client.on('error', console.error);
У меня сейчас те же проблемы. Использование retry_strategy, возврат ошибки, как указано в примере в Readme, но клиент не выдает ошибки. Исправления, предложенные @ v1adko, решают проблему хотя бы номинально.
Мне интересно, что это за обратная несовместимость, упомянутая здесь?
https://github.com/NodeRedis/node_redis/blob/009479537eb920d2c34045026a55d31febd1edd7/index.js#L380
Как указывает @maael , поведение кажется преднамеренным, когда установлено retry_strategy. Ожидается ли такое поведение, но документация неверна? Должен ли я выдавать ошибки для клиента вручную, как это предлагает @ c24w ?
edit: Когда я копаюсь в пакете, я понимаю, что испускание вручную, вероятно, не путь вперед. Кажется, мне нужно понять упомянутые критические изменения.
Какие-нибудь Новости ? У меня точно такая же проблема.
Какие-нибудь Новости?
это неправильная идея:
js
if (options.error && options.error.code === 'ECONNREFUSED') {
// End reconnecting on a specific error and flush all commands with
// a individual error
return Math.min(options.attempt * 100, 3000);
}
Имея ту же проблему, retry_Strategy не запускает событие ошибки, еще не исправлено?
У кого-нибудь получилось?
Вместо этого мы переключили нашу реализацию на https://github.com/luin/ioredis , что принесло несколько улучшений (собственные Promises, lazyConnect (избегайте открытия соединения при создании экземпляра клиента Redis, помогло нам обрабатывать ошибки именно там, где нам было нужно)), и позволяет запускать следующий код:
let cachedItem;
try {
logger.debug(`Fetching GraphCMS query in redis cache...`);
// XXX If fetching data from redis fails, we will fall back to running the query against GraphCMS API in order to ensure the client gets the data anyway
cachedItem = await redisClient.get(body);
} catch (e) {
logger.debug(`An exception occurred while fetching redis cache.`);
logger.error(e);
epsagon.setError(e);
}
Используя следующий utils/redis.js
:
import { createLogger } from '@unly/utils-simple-logger';
import Redis from 'ioredis';
import epsagon from './epsagon';
const logger = createLogger({
label: 'Redis client',
});
/**
* Creates a redis client
*
* <strong i="11">@param</strong> url Url of the redis client, must contain the port number and be of the form "localhost:6379"
* <strong i="12">@param</strong> password Password of the redis client
* <strong i="13">@param</strong> maxRetriesPerRequest By default, all pending commands will be flushed with an error every 20 retry attempts.
* That makes sure commands won't wait forever when the connection is down.
* Set to null to disable this behavior, and every command will wait forever until the connection is alive again.
* <strong i="14">@return</strong> {Redis}
*/
export const getClient = (url = process.env.REDIS_URL, password = process.env.REDIS_PASSWORD, maxRetriesPerRequest = 20) => {
const client = new Redis(`redis://${url}`, {
password,
showFriendlyErrorStack: true, // See https://github.com/luin/ioredis#error-handling
lazyConnect: true, // XXX Don't attempt to connect when initializing the client, in order to properly handle connection failure on a use-case basis
maxRetriesPerRequest,
});
client.on('connect', function () {
logger.info('Connected to redis instance');
});
client.on('ready', function () {
logger.info('Redis instance is ready (data loaded from disk)');
});
// Handles redis connection temporarily going down without app crashing
// If an error is handled here, then redis will attempt to retry the request based on maxRetriesPerRequest
client.on('error', function (e) {
logger.error(`Error connecting to redis: "${e}"`);
epsagon.setError(e);
});
return client;
};
И utils/redis.test.js
файл:
import { getClient } from './redis';
let redisClient;
let redisClientFailure;
describe('utils/redis.js', () => {
beforeAll(() => {
redisClient = getClient();
redisClientFailure = getClient('localhost:5555', null, 0); // XXX This shouldn't throw an error because we're using lazyConnect:true which doesn't automatically connect to redis
});
afterAll(async () => {
await redisClient.quit();
await redisClientFailure.quit();
});
describe('should successfully init the redis client', () => {
test('when provided connection info are correct', async () => {
// Environment variables are from the .env.test file - This tests a localhost connection only
expect(redisClient.options.host).toEqual(process.env.REDIS_URL.split(':')[0]);
expect(redisClient.options.port).toEqual(parseInt(process.env.REDIS_URL.split(':')[1], 10));
expect(redisClient.options.password).toEqual(process.env.REDIS_PASSWORD);
});
test('when connection info are incorrect', async () => {
expect(redisClientFailure.options.host).toEqual('localhost');
expect(redisClientFailure.options.port).toEqual(5555);
});
});
describe('should successfully perform native operations (read/write/delete/update)', () => {
test('when using async/await (using native node.js promises)', async () => {
const setResult = await redisClient.set('key-1', 'value-1');
expect(setResult).toEqual('OK');
const result = await redisClient.get('key-1');
expect(result).toEqual('value-1');
const delResult = await redisClient.del('key-1');
expect(delResult).toEqual(1);
const setResultB = await redisClient.set('key-1', 'value-1b');
expect(setResultB).toEqual('OK');
const resultB = await redisClient.get('key-1');
expect(resultB).toEqual('value-1b');
const setResultC = await redisClient.set('key-1', 'value-1c');
expect(setResultC).toEqual('OK');
const resultC = await redisClient.get('key-1');
expect(resultC).toEqual('value-1c');
});
});
describe('should allow to catch an error when failing to open a connection to redis, in order to gracefully handle the error instead of crashing the app', () => {
test('when connection info are incorrect', async () => {
expect(redisClientFailure.options.host).toEqual('localhost');
expect(redisClientFailure.options.port).toEqual(5555);
try {
await redisClientFailure.set('key-1', 'value-1'); // This should throw an error, because the connection to redis will be made when executing the
expect(true).toBe(false); // This shouldn't be called, or the test will fail
} catch (e) {
expect(e).toBeDefined();
expect(e.message).toContain('Reached the max retries per request limit');
}
await redisClientFailure.quit();
});
});
});
Переменные env:
REDIS_URL=localhost:6379
REDIS_PASSWORD=mypasswordissostrong
Самый полезный комментарий
Какие-нибудь Новости ? У меня точно такая же проблема.