背景

前两天Redis cluster 集群节点宿主机故障,等故障主机恢复后,我启动实例重新加入集群后,因为业务服务器配置的原因,新加入节点连接数瞬间被打满,几番重启后,发现在执行redis-cli --cluster check时,集群出现nodes don’t agree about configuration 。字面意思是有节点保存的集群状态不一致。但具体是什么不一致,百度一番还是有点一头雾水。

源代码分析

由于是使用的5.0.7版本的redis-cli 做的集群管理。不方方便查看源代码,想到低版本的redis-trib.rb 这个ruby脚本逻辑和5.0.7 的redis-cli 几乎一致。于是从redis-trib.rb 查看源代码,首先检索关键字 don’t agree about configuration :

632     def check_config_consistency
 633         if !is_config_consistent?
 634             cluster_error "[ERR] Nodes don't agree about configuration!"
 635         else
 636             xputs "[OK] All nodes agree about slots configuration."
 637         end
 638     end
 639 
 640     def is_config_consistent?
 641         signatures=[]
 642         @nodes.each{|n|
 643             signatures << n.get_config_signature
 644         }
 645         return signatures.uniq.length == 1
 646     end

发现主要由函数 is_config_consistent 检测集群配置是否一致。ruby 咱也不会,具体语法可以现场查询。is_config_consistent 主要逻辑是通过get_config_signature方法将每个node的配置信息去重并写入到 signatures 列表中,最后如果sinagures列表的长度大于1则表示集群内至少有一个节点的配置信息和其他节点不一致。

再根据 get_config_signature 检索redis-trib.rb,查看这个方法具体做什么事情

269     def get_config_signature
 270         config = []
 271         @r.cluster("nodes").each_line{|l|
 272             s = l.split
 273             slots = s[8..-1].select {|x| x[0..0] != "["}
 274             next if slots.length == 0
 275             config << s[0]+":"+(slots.sort.join(","))
 276         }
 277         config.sort.join("|")
 278     end

根据 get_config_signature 函数定义,发现全是关于slot的。大概意思应该是从当前节点所存储的集群信息里,过滤出每个node和它对应的slot,并记录到config 列表并排序。

定位配置不一致的地方

至此,基本确定是有节点存储的集群信息(cluster nodes)里,记录和节点和槽位的分布于其他节点不一致。于是通过如下命令分别获取每个实例所存储的集群信息情况:

redis-cli -h ip -p port -a password cluster nodes  > ip-port.nodes  2>/dev/null

后面对比分析具体的实例与slot的对应关系就是体力活了,不同人用不同的办法。

问题处理

很庆幸,经过对比,发现是一台slave节点 cluster nodes 信息里,记录的有两个master节点的槽位错乱导致。解决过程是直接kill 掉slave进程,手动修改该slave实例的nodes.conf(配置文件cluster-config-file定义的名字) 文件,将node与slot的信息修改为和其他节点一致,然后重启slave节点。再次 check 集群状态,发现ALL nodes agree about configuration !搞定。