由于受网络和运行环境的影响,应用程序可能会遇到暂时性的故障,例如瞬时的网络抖动、服务暂时不可用、服务繁忙导致超时等。通过设计自动重试机制可以大幅避免此类故障,保障操作的成功执行。

引发暂时性故障的原因

原因 说明
故障触发了高可用机制 云数据库Redis支持节点健康状态监测,当监测到实例中的主节点不可用时,会自动触发主备切换,例如将主节点和从节点进行互换,保障实例的高可用性。此时,客户端可能会遇到下列暂时性故障:
  • 秒级的连接闪断。
  • 30秒内的只读状态(用于避免主备切换引起潜在的数据丢失风险和双写)。
说明 更多信息,请参见主备切换的原因和影响
慢查询引起了请求堵塞 执行时间复杂度为O(N)的操作,引发慢查询和请求的堵塞,此时,客户端发起的其他请求可能出现暂时性失败。
复杂的网络环境 由于客户端与Redis服务器之间复杂网络环境引起,可能出现偶发的网络抖动、数据重传等问题,此时,客户端发起的请求可能会出现暂时性失败。

推荐的重试准则

重试准则 说明
仅重试幂等的操作 由于超时可能发生在下述任一阶段:
  • 该命令由客户端发送成功,但尚未到达Redis。
  • 命令到达Redis,但执行超时。
  • 命令在Redis中执行结束,但结果返回给客户端时发生超时。

如果执行重试可能导致某个操作在Redis中被重复执行,因此不是所有操作均适合设计重试机制。通常推荐仅重试幂等的操作,例如SET操作,即多次执行SET a b命令,那么a的值只可能是b或执行失败;如果执行LPUSH mylist  a则不是幂等的,可能导致mylist中包含多个a元素。

适当的重试次数与间隔 根据业务需求和实际场景调整适当的重试次数与间隔,否则可能引发下述问题:
  • 如果重试次数不足或间隔太长,应用程序可能无法完成操作而导致失败。
  • 如果重试次数过大或间隔过短,应用程序可能会占用过多的系统资源,且可能因请求过多而堵塞在服务器上无法恢复。

常见的重试间隔方式包括立即重试、固定时间重试、指数增加时间重试、随机时间重试等。

避免重试嵌套 避免重试嵌套,否则可能会导致重复的重试且无法停止。
记录重试异常并打印失败报告 在重试过程中,建议在WARN级别上打印重试错误日志,同时,仅在重试失败时打印异常信息。

Jedis客户端

  • 在普通JedisPool模式下,Jedis不提供重试功能,推荐使用阿里云的TairJedis(基于Jedis封装),其中封装了Jedis重试类,可快捷实现重试策略。
    说明 如果您的实例为企业版(性能增强型),使用该客户端可以更便捷地使用阿里云自研的数据结构。关于数据结构模块的相关介绍,请参见企业版(性能增强型)支持的新命令
  • 在JedisCluster模式下,您可以配置maxAttempts参数来定义失败时的重试次数(默认值为5)。

重试示例如下:

//添加依赖
<dependency>
  <groupId>com.aliyun.tair</groupId>
  <artifactId>alibabacloud-tairjedis-sdk</artifactId>
  <version>填入最新的版本号</version>
</dependency>

//设置key value命令自动重试5次,并且总体重试时间不超过10s,每次重试在类索引之间等待一段时间,如果结束不成功则抛出异常。

int maxRetries = 5; // 最大重试次数
Duration maxTotalRetriesDuration = Duration.ofSeconds(10); // 最大的重试时间,单位为秒
try {
    String ret = new JedisRetryCommand<String>(jedisPool, maxRetries, maxTotalRetriesDuration) {
        @Override
        public String execute(Jedis connection) {
            return connection.set("key", "value");
        }
    }.runWithRetries();
} catch (JedisException e) {
     // Indicates that maxRetries attempts have been made or the maximum query time maxTotalRetriesDuration reached. 
    e.printStackTrace();
}

Redisson客户端

Redisson客户端提供了两个参数来控制重试逻辑:

  • retryAttempts:重试次数,默认为3。
  • retryInterval:重试间隔,默认为1,500毫秒。

重试示例如下:

Config config = new Config();
config.useSingleServer()
    .setTimeout(1000)
    .setRetryAttempts(3)
    .setRetryInterval(1500) //ms
    .setAddress("redis://127.0.0.1:6379");
RedissonClient connect = Redisson.create(config);

StackExchange.Redis

StackExchang.Redis客户端目前仅支持重试时连接,重试示例如下:

var conn = ConnectionMultiplexer.Connect("redis0:6380,redis1:6380,connectRetry=3");
说明 如需实现API级别的重试策略,请参见Polly

Lettuce

Lettuce客户端未提供在命令超时后重试的参数,但是您可以通过下述参数来实现命令重试策略:

  • at-most-once execution:命令最多执行1次,即0次或1次,如果连接断开并重新连接,命令可能会丢失。
  • at-least-once execution(默认):最少成功执行1次,即可能会在执行时进行多次尝试,保障最少成功执行1次。使用此策略时,如果Redis实例发生了主备切换,此时客户端可能累积了较多的重试命令,主备切换完成后可能会引发Redis实例的CPU使用率激增。
说明 更多信息,请参见Client-OptionsCommand execution reliability

重试示例:

clientOptions.isAutoReconnect() ? Reliability.AT_LEAST_ONCE : Reliability.AT_MOST_ONCE;