背景
- 项目混合JAVA和C++
- C++的业务脱敏就是监听xxx的发生,无状态得诚实执行计算
- 公司C++不太行,封闭不易连Redis集群,封闭不会连MQ,还懒狗不愿意做数据库CAS取事件的方案
- 形势比人强,时间紧,无奈申请一个小哨兵Redis,使用Redis的list给他们当事件队列用
结果/现象:
(1)发现Redisson异常打印过多的日志
(2)哨兵Redis稳定性堪忧,过几天挂一次,运维反馈为连接数过多。
(3)运维还反馈过,JAVA应用中,大量打印哨兵地址相关日志
定位
- JAVA日志脱敏包括
1.大量连接初始化,不管有没有业务操作
[redisson-netty-4-8] INFO o.r.c.pool.SlaveConnectionPool - 12 connections initialized for 哨兵域名:6379/fdef:abcd:0:0:0:0:3e:6666:6379[java应用名]
[redisson-netty-4-24] INFO o.r.c.SentinelConnectionManager - sentinel: redis://真实实例地址:26379 added
2.大量SLAVE下线上线切换--对应源码位置,这里是已经定位修复后改成了trace,不然原先日志打的太多了
当时定位到,每次打印的都会漂移1、2个节点信息,一会儿域名一会儿ip,ip一直变
- 根据日志寻找调用源头
- 可以看到是初始化时 或 周期性刷新哨兵信息时 执行“SENTINEL SENTINELS MYMASTER”得到了哨兵信息后,注册哨兵信息时打印的。
初步分析1:
1.未看到Redisson注册时对旧有注册的哨兵信息及连接的清理
2.国产容器平台提供订阅的哨兵Reids资源,存在IP漂移问题。导致JAVA应用不断创建新连接
继续排查2:
1.slave down 日志定位到源头是周期性检查变更,其中iterrator就是上文registry时用到的一个sentienls(Map)的迭代器
2.checkState再往下找到updateState
(1)首先连接master
(2)然后触发递归调度
masterFuture.onComplete(commonListener);
(3)正主:不满足条件的(没continue)会被加入futures,后面就是salve down 这些
这里其实就有2种可能了
1是后面尝试关,但网络漂移/没keepAlive/DNS都变了/域名无效这类类似原因导致没关掉;
2是redisson设计时没考虑这种sentinel sentinels masterName /salves等命令的返回都是一直漂ip的情况,只会去根据返回值判断要不要下线slave。老的节点在java里就被搁置了,可能还会被使用但不会被关闭,一直堆积就完蛋。
(4)我们先看第1种可能最后就是对这些slave down
但是slave down中有对于slave entry的关闭操作
最终找到关闭的地方,其实是异步的,也没有去最后关心check下关闭的结果如何, 那么这里如果关闭出现异常,就会什么都不打印,和没事一样
(5)我们再看下第2种情况,也是会shutdown的,底层逻辑是和第1种一样的关闭方法
那么得出阶段性结论3:
- Redisson的SentinelConnnectionManager会周期性检查集群哨兵状态
- 根据redis slave命令得到集群信息,判断状态位进行正常shutdown连接
- 根据前后的哨兵地址差异进行冗余shutdown失效的节点
分析均导向了,redisson自身没有问题,可能是磐基平台漂移的ip导致java在shutdown连接时,无法成功shutdown。
而与此同时哨兵资源那里却还以为有人连着,那就会等到超时时间才会关闭。
- 现状是使唤不动磐基,提供方摆烂嘛……
- 现状是不停的连接嘛,那就让漂移后旧的死连接快速过期,让哨兵redis主动快速杀连接来保证连接数不超。即减小服务端超时时间。 原先的几天一挂可能为赶上了业务高峰和那些死连接加一起太多了
- 我知道这很土,那咋办嘛?
ok收工生效成功
总结
- 不要先怀疑开源组件,人家比国产化靠谱多了
- 资源评估时应尽早提供demo应用用于验收暴露问题