Redis(ioredis)-无法捕获连接错误以正常处理它们

时间:2019-08-10 19:37:50

标签: javascript redis ioredis

我正试图妥善处理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以便处理呼叫者上的错误。

但是,当我将网址定义为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)处理的,:) resize.c and bmp.h exist. :) resize.c compiles. :) doesn't resize small.bmp when n is 1 :( resizes small.bmp correctly when n is 2 expected 0xae, not 0x5a in header field bfSize :( resizes small.bmp correctly when n is 3 expected 0x132, not 0x5a in header field bfSize :( resizes small.bmp correctly when n is 4 expected 0x1e6, not 0x5a in header field bfSize :( resizes small.bmp correctly when n is 5 expected 0x306, not 0x5a in header field bfSize :( resizes large.bmp correctly when n is 2 expected 0x6f6, not 0x1e6 in header field bfSize :( resizes smiley.bmp correctly when n is 2 expected 0x336, not 0xf6 in header field bfSize :( resizes smiley.bmp correctly when n is 3 expected 0x6f6, not 0xf6 in header field bfSize``` 是异步的,被调用方不会抛出错误,这不允许调用方使用try / catch处理错误。

是否应该以非常特殊的方式处理redis错误?像我们通常对大多数错误所做的那样,是否有可能抓住它们?

此外,似乎redis重试20次连接(默认情况下),然后引发致命异常(进程已停止)。但我想处理任何异常并以自己的方式处理。

我已经通过提供错误的连接数据测试了Redis客户端的行为,由于该URL上没有可用的Redis实例,因此无法进行连接,我的目标是最终捕获各种Redis错误并优雅地处理它们。

2 个答案:

答案 0 :(得分:2)

客户端import random import numpy as np import tensorflow as tf from keras.optimizers import SGD network = tf.keras.models.Sequential() network.add(tf.keras.layers.Flatten()) network.add(tf.keras.layers.Dense(750, activation=tf.nn.relu)) network.add(tf.keras.layers.Dense(500, activation=tf.nn.relu)) network.add(tf.keras.layers.Dense(100, activation=tf.nn.relu)) network.add(tf.keras.layers.Dense(25, activation=tf.nn.softmax)) network.compile(optimizer='SGD', loss='binary_crossentropy', metrics=['accuracy']) network.fit(scaled_x_train, y_train, epochs=100, batch_size=50) 对象上的连接错误are reported as an error event

根据"Auto-reconnect" section of the docs,当与Redis的连接丢失(或者可能首先无法建立)时,ioredis将自动尝试重新连接。仅在尝试import random import numpy as np from sklearn.preprocessing import MinMaxScaler x_train = [] y_train = [] for m in range(100000): #creating a data set with m items in it grid = [[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]] hints = [[[],[],[],[],[]],[[],[],[],[],[]]] for i in range(5): for j in range(5): grid[i][j] = random.randint(0,1) #All items in the grid are random, either 0s or 1s sub_y_train = [] for z in range(5): for x in range(5): sub_y_train.append(grid[z][x]) sub_y_train = np.array(sub_y_train) y_train.append(sub_y_train) #the grids are added to the data set first ##figuring out the hints along the vertical axis for i in range(5): counter = 0 for j in range(4): if grid[i][j] == 1: counter += 1 if grid[i][j+1] == 0: hints[0][i].append(counter) counter = 0 if grid[i][4] == 1: hints[0][i].append(counter+1) counter = 0 ##figuring out the hints along the horizontal axis for i in range(5): counter = 0 for j in range(4): if grid[j][i] == 1: counter += 1 if grid[j+1][i] == 0: hints[1][i].append(counter) counter = 0 if grid[4][i] == 1: hints[1][i].append(counter+1) counter = 0 for i in range(2): for j in range(5): while len(hints[i][j]) != 3: hints[i][j].append(0) new_hints = [] for i in range(2): for j in range(5): for k in range(3): new_hints.append(hints[i][j][k]) new_hints.append(5) x_train.append(new_hints) #Once the hints are created and formalized, they are added to x_train x_train = np.array(x_train) #Both x_train and y_train are converted into numpy arrays y_train = np.array(y_train) scaler = MinMaxScaler(feature_range=(0,1)) scaled_x_train = scaler.fit_transform((x_train)) for i in range(5): print(scaled_x_train[i]) print(y_train[i]) 之后,才会“挂起有错误的命令”,即在此处进入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 返回的错误。

答案 1 :(得分:2)

这是我的团队在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();
  }