源码分析:go-redis 集群客户端 key 路由的实现
源码:https://github.com/go-redis/redis/tree/v8.11.4
数据结构
flowchart LR
subgraph clusterClient
n["nodes *clusterNodes"]
s["state *clusterState"]
end
subgraph sCNs["clusterNodes"]
n2["nodes map[string]*clusterNode"]
end
subgraph sCS["clusterState"]
s2["slots []*clusterSlot"]
gg["generation uint32"]
ct["createdAt time.Time"]
end
subgraph sCSt["clusterSlot"]
se["start, end int"]
n3["nodes []*clusterNode"]
end
subgraph sCN["clusterNode"]
c["Client *Client"]
g["generation uint32"]
end
n --> sCNs
s --> sCS
s2 --> sCSt
n2 --> sCN
n3 --> sCN
gg -.- g
- clusterClient:集群客户端抽象
- clusterNodes:维护集群内「实例地址=>节点」的映射
- clusterState:维护集群内「哈希槽=>节点」的映射,创建后不可修改,只能通过新建替换更新,每次新建 generation 自增。createdAt 为创建时间。
- clusterSlot:一个范围的哈希槽以及负责这些槽的节点(第一个为主节点,其余为从节点),start为起始哈希槽编号,end为起结束希槽编号,nodes为负责这些槽的节点。
- clusterNode:集群节点抽象。client为连接此节点的客户端,generation与clusterState的generation关联。
算法
flowchart TD
A[执行命令] --> B{"clusterClient.state<br/>是否存在?"};
B -->|不存在| C["使用 CLUSTER SLOTS 命令
获取「哈希槽=>实例地址」的映射信息"];
C --> D["根据「哈希槽=>实例地址」的映射信息创建clusterState,
clusterState.createdAt 记录当前时间,
设置最新的 clusterState.generation,
根据实例地址,请求clusterClient.nodes创建或获取clusterNode;
设置clusterNode.generation为clusterState.generation,
clusterSlot.nodes引用这些clusterNode"]
D --> E["根据命令key计算slot编号,根据slot编号
定位 clusterSlot = clusterClient.state.slots[*]"]
E --> F["使用clusterSlot.nodes[0].Client执行命令"]
D -."异步".-> G["运行一次性定时器,一段时间后检查clusterSlot.nodes,
如果其中包含clusterNode.generation小于该
clusterState.generation,移除这些clusterNode,
关闭clusterNode.Client释放资源"]
B -->|存在| H[" "] --> E
H -."如果clusterState.createdAt(创建时间)
距当前时间超过某阈值,异步执行".-> S
F --"如果返回MOVED重定向"--> I["根据重定向提供的实例地址,
请求clusterClient.nodes创建或获取clusterNode,
使用clusterNode.Client执行命令"]
I -.->|异步执行| S
subgraph S["Create/Update state"]
C
D
G
end