首页 > 解决方案 > 为什么我在使用超时选项值(50 秒)小于 redis 命令超时(200 秒)设置的 brpop 命令时出现 redis 命令超时?

问题描述

我使用 redis list 作为分布式阻塞队列。在客户端,我使用以下代码:

public String tryAquire(String appName, long timeout, TimeUnit timeUnit){
  return String.valueOf(redisTemplate.opsForList.rightPop(getKey(appName), timeout, timeUnint)); 
}

它使用brpop内部命令,超时值设置为小于 50 秒。这项服务可以正常工作大约两周,直到最近的最后 2 天我得到了一些例外情况:

org.springframework.QueryTimeoutException:Redis command time out;
nest exception is io.lettuce.core.RedisCommandException:
Command time out after 200 seconds(s)

此异常每天出现 1 或 2 次,每天大约有 2000 个请求,在此异常之后,服务器仍然可以正常工作,后续请求的花费时间变得正常,但抛出此异常的请求将花费超过 200 秒,这是一个非常糟糕的情况。

这个超时值(200 秒)适合我的生菜客户端命令超时设置。

生菜客户端设置

但是,对于该tryAquire(appName, timeout, timeUnit)方法,最大阻塞时间设置为小于 50 秒。因此,这个命令不应该花费超过 50 秒的时间,因为 50 秒后如果 redis 列表中没有元素,它应该只返回 null 而不是继续等待。似乎没有网络问题,因为日志中没有与套接字相关的异常,并且在 redis 命令超时异常之后,后续请求执行成功。

标签: javaredisspring-data-redis

解决方案


以防万一有人遇到同样的问题。长话短说:NAT 映射过期,并且在达到最大 tcp 重试次数后,应用程序会发现断开的网络链接。

该服务部署在使用定制 SDN 的云环境中。如果一段时间内 tcp 连接上没有活动,则 NAT 映射过期。但是,tcp 客户端和服务器都无法意识到这一点,即使 NAT 映射已过期,客户端也会继续尝试发送数据,直到达到某些 tcp 重试最大限制配置。

一个简单的解决方案:只需将 redis 服务器的“tcp-keepalive”配置值设置为小于 NAT 映射过期时间,或者如果您不想更改 redis 服务器设置,请使用任何其他心跳机制。


推荐阅读