[英]Redis (ioredis) - Unable to catch connection error in order to handle them gracefully
我正在嘗試優雅地處理 redis 錯誤,以便繞過錯誤並執行其他操作,而不是使我的應用程序崩潰。
但到目前為止,我不能只捕獲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 錯誤並優雅地處理它們。
連接錯誤在客戶端Redis
對象上報告為error
事件。
根據文檔的“自動重新連接”部分,ioredis 將在與 Redis 的連接丟失時自動嘗試重新連接(或者,大概無法首先建立)。 只有在maxRetriesPerRequest
嘗試之后,掛起的命令才會“被錯誤刷新”,即在這里catch
:
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);
}
由於您在第一個錯誤時停止程序:
client.on('error', function (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
...重試和隨后的“因錯誤而刷新”永遠沒有機會運行。
忽略client.on('error'
,你應該得到從await redisClient.get()
返回的錯誤。
以下是我的團隊在 TypeScript 項目中使用 IORedis 所做的事情:
let redis;
const redisConfig: Redis.RedisOptions = {
port: parseInt(process.env.REDIS_PORT, 10),
host: process.env.REDIS_HOST,
autoResubscribe: false,
lazyConnect: true,
maxRetriesPerRequest: 0, // <-- this seems to prevent retries and allow for try/catch
};
try {
redis = new Redis(redisConfig);
const infoString = await redis.info();
console.log(infoString)
} catch (err) {
console.log(chalk.red('Redis Connection Failure '.padEnd(80, 'X')));
console.log(err);
console.log(chalk.red(' Redis Connection Failure'.padStart(80, 'X')));
// do nothing
} finally {
await redis.disconnect();
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.