Node.js 到 Redis 的 ECONNRESET 错误

12

我们在Node.js和Redis连接中使用Redis npm模块。

偶尔,我们会遇到以下错误:

message: read ECONNRESET, stack: Error: read ECONNRESET
at exports._errnoException (util.js:1020:11)
at TCP.onread (net.js:568:26)

Redis的设置如下:一个Redis服务器在内部网络中,两个Node.js服务器位于DMZ中,每个服务器均有8个PM2实例。节点服务器与Redis服务器之间有防火墙。

NODE版本-6.11.2,REDIS版本-3.2.9,PM2版本-2.4.6

我们进行了TCP转储(TCP dump)。转储显示了一些RST/ACK数据包。TCP转储

在Node.js中,我们创建了一个单一的Redis连接,并尝试将相同的Redis连接用于所有请求。

    const Redis = require('redis');
    const Config = require('../../config');
    const Logger = require('../helpers/logger');

    const redisClient = (function() {

       // Start with a fake client so that we have a client that works
       // even when Redis server is down
      let client = {
        get: function(key, callback) {
          callback(null, null);
        },
        setex: function(key, time, value) {
          Logger.info('Value:',value);
          // Do nothing in particular
        }
      };
       // Attempt to create a new instance of an actual redis client
      const redisConfig = Config.get('/redis');
      const tempClient = Redis.createClient(redisConfig.port,redisConfig.host, {
        //eslint-disable-next-line
        // console.log('[redis]','Creating the retry strategy');
        retry_strategy: function(options) { //eslint-disable-line
          //eslint-disable-next-line
          console.log('[redis]','Creating the retry strategy');
          if (options.error && options.error.code === 'ECONNREFUSED') {
              // End reconnecting on a specific error and flush all commands with
              // a individual error
            //eslint-disable-next-line
            console.log('[redis,error]','Connection refused error');
            return new Error('The server refused the connection');
          }
          if (options.error && options.error.code === 'NR_CLOSED') {
            //eslint-disable-next-line
            console.log('[redis,error]','Connection closed error');
            return new Error('The server closed the connection');
          }
          if (options.attempt > 5) {
              // End reconnecting with built in error
            //eslint-disable-next-line
            console.log('Exceeded attempts');
            return undefined;
          }
          if (options.total_retry_time > 1000 * 60 * 60) {
            // End reconnecting after a specific timeout and flush all commands
            // with a individual error
            //eslint-disable-next-line
            console.log('Retrial time:' + options.total_retry_time);
            return 1000;
          }
          // reconnect after
          return Math.min(options.attempt * 100, 3000);
        }
      });

       // Set the "client" variable to the actual redis client instance
       // once a connection is established with the Redis server
      tempClient.on('ready', () => {
        client = tempClient;
      });
      tempClient.on('error', (error) => {
        Logger.info(['redis','error'],'Redis client error:', error);
        if (error.code === 'NR_CLOSED') {
          tempClient.end();
          client = Redis.createClient(redisConfig.port,redisConfig.host, {
            retry_strategy: function(options) { //eslint-disable-line
              if (options.error && options.error.code === 'NR_CLOSED') {
                Logger.info(['redis','error'],'Connection closed error');
                return new Error('The server refused the connection');
              }
            }
          });
        }
      });

       /**
        * Get a redis client
        * @return {Object} client - eventually a proper redis client object
        * (if redis is up) or a fake client object (if redis is down)
        */
      const getClient = function() {
        Logger.info('Getting the client ' + client);
        return client;
      };

      return {
        getClient: getClient
      };

    }());

    module.exports = redisClient;

我们想知道导致连接问题的具体原因,以及为什么会出现这种情况,应该采取什么解决措施。


你好,你最近解决了这个问题吗? - Va5ili5
following as well... - Brian Zhou
你是怎么解决这个问题的? - Taylor Clark
2个回答

2
在/etc/redis/redis.conf文件中,将bind 127.0.0.1替换为bind 0.0.0.0, 将protected-mode yes替换为protected-mode no。然后使用ufw allow 6379和ufw allow 6379/tcp允许端口6379。接着输入以下命令: iptables -A INPUT -p tcp --dport 6379 -j ACCEPT iptables -A INPUT -p udp --dport 6379 -j ACCEPT 最后执行以下命令: sudo systemctl stop redis sudo systemctl start redis

1
高安全风险:您的Redis服务器将暴露在互联网上。 - Udo E.
你是对的,必须打开保护模式并设置授权密钥。 - Mucahid Uslu

1
我在使用Google Firebase Cloud Functions时遇到了同样的问题。问题在于Redis的最新版本会一直保持连接,如果调用Redis的进程在短时间内结束,就像我们的Cloud Functions一样,您必须将timeout设置为与默认值0不同的值。
您可以通过更改名为redis.conf的Redis配置文件中的超时值来实现此操作。
文档还表示,您可以在不重新启动实例的情况下设置超时设置,但如果您的Redis实例受到保护,因此无法运行像CONFIG SET timeout 15这样的命令,则无法实现此操作,因为您会收到以下错误消息:

(error) ERR unknown command CONFIG


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接