我有下面的代码并尝试使用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 跟踪脚本:Redis 客户端:连接
21:00:14.695Z 跟踪脚本:Redis 客户端:准备就绪
21:10:23.837Z 跟踪脚本:Redis 客户端:结束
重试策略检查
{ 尝试:1,
错误:{ [错误:Redis 连接到 redis.callision。 信息:6379失败 - 读取 ECONNRESET] 代码: 'ECONNRESET', errno: 'ECONNRESET', syscall: 'read' },
total_retry_time: 0,
连接时间:1 }/node_modules/q/q.js:155
扔e;
^
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)
在套接字。(/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 的输入
我还看到,在第一次失败的连接上会发出 'end' 事件,这可能意味着什么(或不是),我两天前拿起了 Redis,所以不确定内部是如何工作的。 如果/当我有时间时,我会尝试分叉和挖掘更多。
从字面上看,下一个问题似乎与这个问题有关 #1198
@v1adko我目前正在旅行,但我会试着在今天晚些时候或明天看看它(除非鲁本打败了我)。
我故意将我的 redis url 错误地测试错误场景,但是我看到在尝试连接到 redis 时没有调用我的 retry_strategy。 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;
}
constqualifyUrl = (url) => {
return '//' + url.replace(/^\/+/,"");
};
`
有人可以帮我解决这个问题。
同样在这里。 这个讨厌的黑客似乎创造了预期的行为,但不确定它是否有更广泛的影响:
const client = redis.createClient({
retry_strategy: ({error}) => client.emit('error', error)
});
client.on('error', console.error);
我目前遇到了同样的问题。 使用 retry_strategy,返回自述文件中示例所示的错误,但客户端没有发出任何错误。 @v1adko提出的修复至少从表面上解决了这个问题。
我想知道这里提到的向后不兼容是什么?
https://github.com/NodeRedis/node_redis/blob/009479537eb920d2c34045026a55d31febd1edd7/index.js#L380
正如@maael所指出的,该行为似乎是在设置 retry_strategy 时有意为之。 那么行为是否符合预期,但文档不正确? 我应该按照@c24w 的建议手动为客户端发出错误吗?
编辑:当我深入研究包时,我意识到手动发射可能不是前进的方向。 似乎我需要了解提到的重大变化。
任何新闻 ? 我也有同样的问题。
任何新闻?
是一个错误的想法:
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();
});
});
});
环境变量:
REDIS_URL=localhost:6379
REDIS_PASSWORD=mypasswordissostrong
最有用的评论
任何新闻 ? 我也有同样的问题。