Redis学习记录:spring框架lettuce哨兵接入拓扑更新踩坑记录
最近使用spring-data-redis接入容器化redis进行自测时,踩到一个坑。
本篇文章简要记录一下排坑过程。
一、问题背景
此问题是自测时出现的,整体的流程如下:
K8S容器化部署3实例哨兵集群 + 1主1从主从集群
业务客户端使用
spring-data-redis
库以哨兵模式接入,底层使用lettuce,使用容器化部署使用
delete --force
删除主实例pod,模拟K8S节点宕机场景业务客户端出现timeout报错,持续重试
观测哨兵信息,发现已经主从切换完成,哨兵信息中旧从节点已经提升为主
持续观察业务客户端,无法自动恢复,仍持续出现timeout报错;重启业务客户端,业务操作恢复正常
二、问题排查过程
此前已经看过lettuce的哨兵相关源码,可以确认lettuce原生逻辑,在这种情况下会监听导致提主事件刷新拓扑。
但是这次在spring框架内使用lettuce,却无法更新lettuce拓扑,仍在不断重试访问已经宕机的旧主。
把日志级别调整为debug模式,发现日志中并没有拓扑刷新的日志,说明拓扑并没有刷新。
这种情况我感觉是spring-data-redis
库有点什么问题,故先去issue中搜索一下。
1 | issue: https://github.com/spring-projects/spring-data-redis/issues/1952 |
经过查询,果然找到一个类似情况的issue,核心信息汇总如下:
lettuce设计上是异步连接,在模拟K8S节点宕机场景时,
Fin
/RST
包无法正常发送,则异步连接短时间内不会自动断开。spring-data-Redis底层为lettuce时,会保持长连接。除非连接断开,否则拓扑不会主动刷新。
如果希望通过spring-data-Redis使用lettuce时持续追踪拓扑变化,则需要配置
ReadFrom
。
所以最终可以确认,是lettuce设计上,默认配置就没兼容这种场景。
通过查看spring-data-redis
相关源码,发现在配置ReadFrom
后,每次执行操作前都会尝试按配置获取最新的主/从实例,这样就不会出现异步连接未断开导致拓扑无法刷新的问题了。
随后再次测试,在业务客户端中配置ReadFrom
,注入故障后,发现业务操作可以恢复正常,由此问题解决。
三、小结
这个问题整体来看有点奇怪,核心是Fin
/RST
包无法正常发送,导致异步连接无法正常关闭刷新拓扑。
但是我感觉spring-data-redis
理应处理这种场景,不应该基于连接来触发拓扑刷新,应该有个额外的刷新机制,结果却没有。
仔细看issue里的回复,开发人员认为额外的刷新可能会导致额外资源消耗。。。好吧,可能后续最佳实践里要提一嘴,spring-data-redis
使用lettuce哨兵模式接入时,记得配置一下ReadFrom
来避免这个问题。