我们都知道redis集群有16384个槽,它会因为我们集群个数配置的不同而分配不同的slot给各个节点,而这篇文章就来聊聊当某个节点处理到非它所负责的slot时是如何处理的,这一点很好的体现了redis对于raft协议良好的设计与实现。
一、详解redis集群指令处理
1. 整体流程
假设我们现在集群中有个节点,每个节点各自负责一部分槽,此时我们的客户端向节点2发起一个set指令,而该指令对应的key应该是要存放到节点1中,对此节点2的做法是查看自己所维护的节点列表是否有负责该slot的节点,如果发现了而回复给客户端move指令,告知客户端到指令的ip端口的节点进行键值对存储:
了解完整体流程之后,我们通过源码的方式来印证这些实现上的细节,我们都知道redis客户端发送的指令都会被redis的processCommand处理,该函数如果发现当前是以集群的方式启动并且符合以下两个条件则以集群的逻辑解析这条指令:
- 发送指令的不是master服务器。
- 参数中带有key。
那么redis就会调用getNodeByQuery查询重定向的节点,如果发现查询到的节点不是自己或者为空则调用clusterRedirectClient进行重定向处理:
2. 详解节点定位步骤getNodeByQuery
步入getNodeByQuery即可看到查询的核心流程,无论是单条还是多条客户端指令,他都会封装成multiState结构体交由后续逻辑处理,而后续逻辑就会遍历这些指令并计算出对应的slot,然后执行如下逻辑:
- 如果发现定位到的节点是自己,且当前节点正在做迁移,则做个迁移标记,然后检查当前节点是否有这个槽,如果没有则发送ASK指令告知客户端重定向到另一个迁移的目标槽试试看。
- 如果对应的key没有找到对应的槽,则直接返回当前节点。
- 找到目标槽,直接返回MOVE指令和目标槽的信息。
对应我们给出getNodeByQuery的核心代码段:
3. 结果告知客户端
上述流程发现处理的节点不是自己之后,调用clusterRedirectClient进行重定向,如果是REDIS_CLUSTER_REDIR_MOVED则告知客户端这些slot后续直接找重定向节点处理就好了,后续无需找自己。若是REDIS_CLUSTER_REDIR_ASK则说明当前节点正处于数据迁移到目标节点,你可以到迁移的节点进行请求,后续再次发起请求是还是找当前节点看看能否出去,如果不能在进行重定向:
二、小结
这篇文章比较精简,我们通过源码的方式简单的剖析了去中心化的redis如何在不同节点处理不同槽的请求,大体过程比较简单:
- 接收并处理客户端传入的key指令操作。
- 通过getNodeByQuery获取key对应的slot所属节点。
- 如果是当前节点的slot直接处理。
- 如果不是则查看是否正在迁出,如果是则返回ask让客户端到别的节点试试看,反之进入步骤5。
- 如果定位的slot对应的节点是别的节点则直接用move指令重定向客户端,让客户端到另一个节点询问结果。