java - 为什么我在使用超时选项值(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 命令超时异常之后,后续请求执行成功。
解决方案
以防万一有人遇到同样的问题。长话短说:NAT 映射过期,并且在达到最大 tcp 重试次数后,应用程序会发现断开的网络链接。
该服务部署在使用定制 SDN 的云环境中。如果一段时间内 tcp 连接上没有活动,则 NAT 映射过期。但是,tcp 客户端和服务器都无法意识到这一点,即使 NAT 映射已过期,客户端也会继续尝试发送数据,直到达到某些 tcp 重试最大限制配置。
一个简单的解决方案:只需将 redis 服务器的“tcp-keepalive”配置值设置为小于 NAT 映射过期时间,或者如果您不想更改 redis 服务器设置,请使用任何其他心跳机制。
推荐阅读
- google-apps-script - 工作表复制脚本:其他用户运行脚本时未设置保护
- php - 如何在codeigniter中将日期格式更改为strtotime
- sql - 返回运行总计字段的上一行值
- angular - 我们可以将 ngx-lightbox 与异步管道一起使用吗?
- arrays - 使用嵌套数组进行数组操作
- javascript - 禁用 Formik 字段的重置值
- javascript - 如何从数组中删除嵌套元素?
- javascript - javascript使对象逐渐移动而不是立即移动
- ruby - ruby 中的模型和模式是否相同?
- .net - IBM MQ .NET (amqmdnetstd) 共享订阅查询