我正在尝试优雅地处理redis错误,以便在错误发生时绕过它并做其他事情,而不是使我的应用程序崩溃。
但到目前为止,我无法捕获ioredis抛出的异常,它绕过了我的
但到目前为止,我无法捕获ioredis抛出的异常,它绕过了我的
try/catch
并终止了当前进程。这种当前行为不允许我优雅地处理错误,并从替代系统(而不是redis)获取数据。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
*
* @param url Url of the redis client, must contain the port number and be of the form "localhost:6379"
* @param password Password of the redis client
* @param 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.
* @return {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);
if (e.message === 'ERR invalid password') {
logger.error(`Fatal error occurred "${e.message}". Stopping server.`);
throw e; // Fatal error, don't attempt to fix
}
});
return client;
};
我正在模拟错误的密码/url,以查看如何处理redis配置不当。为了处理调用方的错误,我将lazyConnect
设置为true
。
但是,当我将url定义为localhoste:6379
(而不是localhost:6379
)时,我会收到以下错误:
server 2019-08-10T19:44:00.926Z [Redis client] error: Error connecting to redis: "Error: getaddrinfo ENOTFOUND localhoste localhoste:6379"
(x 20)
server 2019-08-10T19:44:11.450Z [Read cache] error: Reached the max retries per request limit (which is 20). Refer to "maxRetriesPerRequest" option for details.
这是我的代码:
// Fetch a potential query result for the given query, if it exists in the cache already
let cachedItem;
try {
cachedItem = await redisClient.get(queryString); // This emit an error on the redis client, because it fails to connect (that's intended, to test the behaviour)
} catch (e) {
logger.error(e); // It never goes there, as the error isn't "thrown", but rather "emitted" and handled by redis its own way
epsagon.setError(e);
}
// If the query is cached, return the results from the cache
if (cachedItem) {
// return item
} else {} // fetch from another endpoint (fallback backup)
我的理解是,Redis错误通过 client.emit('error', error)
处理,这是异步的,调用者不会抛出错误,这不允许调用者使用try/catch来处理错误。
Redis的错误是否应该以非常特定的方式处理?难道不能像我们通常处理大多数错误一样捕获它们吗?
此外,默认情况下,Redis在抛出一个致命异常之前会重试连接20次。但我想以自己的方式处理任何异常。
我已经通过提供错误的连接数据测试了Redis客户端的行为,这使得连接无法建立,因为在那个URL上没有可用的Redis实例,我的目标是最终捕获所有类型的Redis错误并优雅地处理它们。
client:on:error
事件中不能有任何throw
,我可能在测试时遇到了一些竞态条件,或者修复了一个错误,但现在这样做很好! - Vadorequest