【问题标题】:Redis connection lost without any indicationRedis 连接丢失,没有任何迹象
【发布时间】:2019-12-14 10:41:45
【问题描述】:

我正在使用一个非常简单的 redis pub-sub 应用程序,其中我在 AWS 中有一个 redis 服务器和一个基于 nodejs 的 redis 客户端,它位于办公室 LAN 内,订阅了某个频道。

这一直很好,直到网络发生变化并且似乎某些设备现在干扰了传出连接(我还开始接收出站 SSH 连接上的套接字挂断,我通过 SSH 配置中的 ServerAliveInterval 60 设置缓解了这种情况)。

在网络发生变化后,每当执行 redis 客户端应用程序时,它都会创建一个 redis 客户端,订阅某个频道并根据该频道中发布的消息进行操作。 它可以正常运行几分钟,但随后停止接收任何消息。

我将 redis 客户端注册到所有已知的连接事件(包括“错误”事件),我添加了一个“retry_strategy”处理程序,并将“socket_keepalive”和“socket_initialdelay”配置修改为 10 秒(见下面的代码)。

但是,当连接受到干扰时,不会触发任何事件。

当应用停止接收消息时,我看到redis端口上的连接仍然有效:

dev@server:~> sudo netstat -tlnpua | grep 6379
tcp        0      0 10.43.22.150:52052      <server_ip>:6379     ESTABLISHED 27014/node

我还在端口 6379 上捕获了一个 PCAP,在该端口上我没有看到任何重置或 TCP 错误,而且从连接的角度来看,一切都是有效的。

我尝试从 LAN 中运行另一个 nodejs 应用程序,在该应用程序中我创建了一个连接到 AWS redis 服务器的客户端,注册所有事件并且只偶尔发布一次消息。 几分钟后(连接中断),我尝试发布另一个命令,确实触发了错误事件处理程序

> client.publish("channel", "ANOTHER TRY")
true
> Error: Redis connection to <server_hostname>:6379 failed - read ECONNRESET
Redis connection ended
Redis reconnecting
Redis connected
Redis connection is ready

因此,如果我在连接受到干扰后尝试通过客户端发布,则确实会调用连接事件回调,并且我可以运行某种重新连接逻辑。

但在我订阅并等待发布到频道的场景中,没有调用任何连接事件处理程序,应用程序基本上被破坏了。

应用代码:

const redis = require('redis');

const config = { "host": <hostname>, "port": 6379, "socket_keepalive": true, 
"socket_initdelay": 10};

config.retry_strategy =  function (options) {
    console.log("retry strategy. error code: " + (options.error ? 
options.error.code : "N/A"));
    console.log("options.attempt", options.attempt, "options.total_retry_time", 
options.total_retry_time);
    return 2000;
}

const client = redis.createClient(config);

client.on('message', function(channel, message) {
    console.log("Channel", channel, ", message", message);
});

client.on("error", function (err) {
    console.log("Error " + err);
});

client.on("end", function () {
    console.log("Redis connection ended");
});

client.on("connect", function () {
    console.log("Redis connected");
});

client.on("reconnecting", function () {
    console.log("Redis reconnecting");
});

client.on("ready", function () {
    console.log("Redis connection is ready");
});

const channel = "channel";
console.log("Subscribing to channel", channel);
client.subscribe(channel);

我正在使用 redis@2.8.0 和节点 v8.11.3。

【问题讨论】:

  • 我在 RSMQ redis 节点模块上遇到了类似的问题,但令人惊讶的是,在 RSMQ-worker 模块上却没有(对于另一个用例)。我将在下面尝试您的解决方案,感谢您发布它。

标签: node.js redis


【解决方案1】:

这个问题的解决方法很悲催。

首先,redis 客户端和服务器之间确实有一些网络设备,在超时后会丢弃不活动的连接。看来这个超时时间真的很低(几分钟)。

Redis 有一个socket_keepalive 配置默认开启,它的默认值是Node.js 的默认socket keep alive 值(如果我没记错的话设置为2 小时)。

从上面可以看出,我使用了一个socket_initdelay 配置参数,它应该更改了这个默认值,但不幸的是,使用这个参数的代码不在redis npm 包中,而是在node-redis 中。

总结一下: redis@2.8.0(写这篇文章时的最新版本)中没有配置设置来更改保持活动超时值。 您可以:

  1. 使用接受socket_initdelay 设置的node-redis

  2. 通过运行以下命令手动修改超时:

const client = redis.createClient();
client.on("connect", function () {
    client.stream.setKeepAlive(true, <timeout_value_in_milliseconds>);
}

【讨论】:

  • 不确定其他版本,但 node-redis 3.0.2 设置了 socket_initial_delay 可用。请注意,您不确定默认值设置为 0 是否使其保持活动状态,或者是否意味着它默认被禁用?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-10
  • 2013-04-27
  • 2021-09-15
相关资源
最近更新 更多