Node-redis: 刷新错误后 retry_strategy 不会重置

创建于 2017-02-18  ·  21评论  ·  资料来源: NodeRedis/node-redis

  • 版本:node_redis 2.6.5,redis 3.2.7
  • 平台:Mac OS 10.12.3 上的 Node.js 7.5.0
  • 说明

我正在使用以下代码

const redis = require('redis');

const client = redis.createClient({
  url: 'redis://localhost:6379',
  retry_strategy: options => new Error('Fail')
});

setInterval(() => client.incr('some-key', console.log), 5000);

针对本地 redis ( docker run --rm -p 6379:6379 redis ) 运行

在我看到第一个输出后,我杀死了 redis 以模拟断开连接。 当我看到错误出现时,我重新启动了 redis。 连接不会再次出现。 我希望在将错误刷新到离线处理程序后,客户端会尝试在下一个命令上重新连接。 相反,它保持关闭状态。 此外,它返回AbortError而不是new Error('Fail')

Feature Request

最有用的评论

在这里快速更新。
我将enable_offline_queue = false到我的createClient选项中,它似乎已经完成了我想要的操作,即:
“继续尝试基于retry_strategy函数重新连接到我的 Redis 服务器,但在此期间尝试使用客户端时立即抛出错误响应。然后,如果能够重新连接,只需重新开始工作”。

仅供参考:在get调用中出现以下错误: AbortError: SET can't be processed. Stream not writeable.

对于我用作数据库和我的应用程序之间的直接层,这应该没问题。 很高兴知道是否有更“强大”的解决方案,其中:

  • 客户端根据retry_strategy继续尝试重新连接
  • 客户端的呼叫/消费者/用户在重新连接时不会挂起
  • 如果/当重新建立连接时,命令仍然可以排队(有条件地?)执行......以防我增加东西等。

这里可能需要考虑很多艰难的场景......

所有21条评论

通过 retry_strategy 的文档,如果函数返回一个数字,node_redis 似乎只会尝试重新连接。

如果您从此函数返回一个数字,则重试将恰好在该时间之后以毫秒为单位进行。

在您的情况下,您返回的是一个非数字,它不满足尝试重新连接的要求。

如果您返回一个非数字,则不会发生进一步的重试,并且所有离线命令都会出现错误。

我正在寻找的流程是:断开连接时,以 1 秒的间隔重试连接 3 次。 如果第三次它仍然断开连接,它应该返回所有未完成的命令并带有错误。 我不希望它停止尝试重新连接。

使用 README.md 中给出的示例的修改版本,您应该能够实现您想要的。 但是,一旦 3 次的最大尝试用完,客户端将不会在下一个命令上自动尝试重新连接。 我相信您需要再次手动调用createClient()

const client = redis.createClient({
    url: 'redis://localhost:6379',
    retry_strategy: (options) => {
        if (options.times_connected >= 3) {
            // End reconnecting after a specific number of tries and flush all commands with a individual error
            return new Error('Retry attempts exhausted');
        }
        // reconnect after
        return 1000;
    }
});

是的,我忽略了这些尝试,因为这在这里没有任何区别。

所以基本上你的意思是:

let client = createClient();
function createClient () {
  return redis.createClient({
    url: 'redis://localhost:6379',
    retry_strategy: (options) => {
      client = createClient();
      return new Error('Retry attempts exhausted');
    }
  });
}

这意味着我不能再在我的代码中传递我的 redis 客户端了?

您可以创建一个轻量级包装器来管理 redis 客户端的单例实例。

// redisClient.js

let redis = require("redis");

let client = null;

const options = {
    url: 'redis://localhost:6379',
    retry_strategy: (options) => {
        client = null;
        return new Error("Redis client connection dropped.");
    }
};

module.exports = {
    getClient: () => {
        if (client == null) {
            client = redis.createClient(options);
        }
        return client;
    }
};

使用:

// usage

let client = require("redisClient.js");

client.getClient().get("some key");
client.getClient().set("some key", "some value");

我想这与在重试策略中调用createClient()非常相似,但我不是那种方法的忠实粉丝。

如果您想减少一直调用getClient()的需要,您可以制作一个大型包装器来包装所有 redis 调用,例如get()set()等,并且在这些方法中的每一种中调用getClient() 。 然而,这种getClient()方法是一种非常常见的懒惰而不是急切地管理连接的方法。

当然,但是如果您无论如何都必须将它包装在额外的重试包装器中,那么为什么这个库根本提供任何重试策略?

我不是作者,所以我不确定 retry_strategy 的具体实现背后的动机。 在我自己使用 node_redis 时,我发现 retry_strategy 的当前功能很有用,但我同意它无论如何都缺乏自动重试的能力。 在我的使用中,我一直从 retry_strategy 返回一个数字并且从不返回错误,因为我总是希望进行连接尝试。 但是,我确实从 retry_strategy 中记录了错误。

这样的事情怎么样? https://github.com/viglucci/node_redis/tree/feature-reconnect-after-flush#retry_strategy -example

var client = redis.createClient({
    retry_strategy: function (options) {
        if (options.error && options.error.code === 'ECONNREFUSED') {
            // End reconnecting on a specific error and flush all commands with a individual error
            return new Error('The server refused the connection');
        }
        if (options.total_retry_time > 1000 * 60 * 60) {
            // End reconnecting after a specific timeout and flush all commands with a individual error
            return new Error('Retry time exhausted');
        }
        // attempt reconnect after retry_delay, and flush any pending commands
        return {
            retry_delay: Math.min(options.attempt * 100, 3000),
            error: new Error('Your custom error.');
        }
    }
});

从概念上讲很有意义。 实现这一点的另一种方法是将其分为两种策略:

var client = redis.createClient({
  // this strategy handles the pending redis commands when the connection goes down
  flush_strategy (options) {
    if (options.attempt >= 3) {
      // flush all pending commands with this error
      return new Error('Redis unavailable')
    }
    // let the connection come up again on its own
    return null;
  },
  // this strategy handles the reconnect of a failing redis
  retry_strategy (options) {
    if (options.total_retry_time > 1000 * 60 * 60) {
      // The connection is never going to get up again
      // kill the client with the error event
      return new Error('Retry time exhausted');
    }
    // attempt reconnect after this delay
    return Math.min(options.attempt * 100, 3000)
  }
});

我真的不喜欢flush_strategy这个名字,但可以想出更好的名字。

哦有趣。 您在这里建议的感觉更像是将当前的retry_strategy重命名为connection_strategy并使用现有的retry_strategy作为您所说的flush_strategy

var client = redis.createClient({
    // this strategy handles reconnection
    connection_strategy (options) {
        if (options.total_retry_time > 1000 * 60 * 60) {
            // The connection is never going to get up again
            // kill the client with the error event
            return new Error('Retry time exhausted');
        }
        // attempt reconnect after this delay
        return Math.min(options.attempt * 100, 3000)
    },
    // this strategy handles the pending redis commands when the connection goes down
    retry_strategy (options) {
        if (options.attempt >= 3) {
            // flush all pending commands with this error
            return new Error('Redis unavailable');
        }
        // let the client attempt the commands once a connection is available
        return null;
    },
});

实施: https :

@Janpot你能详细说明需要的用例吗? 我试图减少选项的数量以使其更易于使用,而retry_strategy已经是一个强大的选项。

@viglucci感谢您在这里回答并提供良好的反馈! 我个人喜欢你的建议,即返回一个对象,以防万一这将被实施。 尽管我计划很快开设一个 v3 分支,但这并不是真正的重大变化。 目前,实现许多新功能非常麻烦,删除所有不推荐使用的东西会有很大帮助。

@BridgeAR现在几乎是这样,如果 redis 连接由于某种原因中断,我的所有命令都会挂起。 假设我对 redis 正在执行的操作有一个回退,那么该回退也会挂起。 我宁愿让 redis 在几次重试后返回一个错误,我可以捕获并使用回退。 这并不意味着我不希望客户端停止尝试连接。 Redis 可能会在一分钟内恢复。

我只是没有看到当前行为的意义。 您在重试处理程序中返回一个错误,然后您的客户端就永远死了。 除非您为每个命令创建一个客户端,否则谁需要这种行为?

或者我可能看不到那个当然的用例😄

@BridgeAR谢谢。 好吧,如果您有一个“通向 V3”的路线图,其中包含一些简单的任务,例如删除对配置参数的支持/删除弃用警告等,那么您有兴趣提交拉取请求。 可能无法处理大型工作,例如添加对其他 redis 命令的支持,但是如果 V3 功能分支可用,我可能会做一些简单的清理工作,如果它们对 PR 开放的话。

@viglucci我邀请作为合作者。 我很快就会制定一个计划

+1 以解决此问题。

我正在使用 Express,虽然我的retry_strategy仍在返回整数以在将来再次尝试连接,但命令(和 Web 请求)继续堆积/备份而不是抛出一些东西以便他们可以得到继续他们的生活......希望在策略说放弃或其他任何事情之前_最终_重新建立联系。

这能实现吗? @Janpot你有没有发现目前可以解决这种情况的任何东西?

也许带有enable_offline_queue和/或retry_unfulfilled_commands选项的东西可以做到这一点?

感谢大家的辛勤工作和帮助!

在这里快速更新。
我将enable_offline_queue = false到我的createClient选项中,它似乎已经完成了我想要的操作,即:
“继续尝试基于retry_strategy函数重新连接到我的 Redis 服务器,但在此期间尝试使用客户端时立即抛出错误响应。然后,如果能够重新连接,只需重新开始工作”。

仅供参考:在get调用中出现以下错误: AbortError: SET can't be processed. Stream not writeable.

对于我用作数据库和我的应用程序之间的直接层,这应该没问题。 很高兴知道是否有更“强大”的解决方案,其中:

  • 客户端根据retry_strategy继续尝试重新连接
  • 客户端的呼叫/消费者/用户在重新连接时不会挂起
  • 如果/当重新建立连接时,命令仍然可以排队(有条件地?)执行......以防我增加东西等。

这里可能需要考虑很多艰难的场景......

@新屋

谢谢你提到这一点。 我们希望我们的应用程序具有相同的行为,您的解决方案可以立即向调用方返回错误,但允许我们无限期地重试连接。

谢谢@newhouse ,我不确定我是否会找到这个。

IMO 这应该是默认行为,或者至少在retry_strategy周围的文档中明确指出。 如果您认为后者合理,请告诉我,我将打开一个 PR 来添加文档。

@bwhitty任何人的文档公关都将是最受欢迎的👍

使用enable_offline_queue = false一个问题是,在调用createClient之后立即发送的命令,但在实际打开连接之前也会失败。

使用enable_offline_queue = false一个问题是,在调用createClient之后立即发送的命令,但在实际打开连接之前也会失败。

@jkirkwood听起来您想推迟尝试发送您的第一个命令,直到发出ready事件(或类似的适当事件)?

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

strumwolf picture strumwolf  ·  4评论

abhaygarg picture abhaygarg  ·  5评论

twappworld picture twappworld  ·  7评论

juriansluiman picture juriansluiman  ·  3评论

betimer picture betimer  ·  5评论