4. 事务请求
[TOC]
本节我们将讲述 etcd集群的事务请求处理过程。首先我们将以put操作为例来讲解一般性事务请求过程。其次,我们将介绍 etcd特有的线性一致性读请求过程。
put 请求
通过 etcdctl put ty dj 命令,向本地监听2380端口的节点发送 写请求。
该请求为GRPC请求,会中转到quotaKVServer进行处理。
Leader接收请求,并广播消息
1 | quotaKVServer.Put |
接下来就进入了主要的处理流程:
1 | node.Propose |
可以看出 将请求两层封装之后传递给通道n.propc。
在node的循环中,其会监听proc通道,当有消息到来时:
1 | node.start | 360+L |
当本节点将需要发送给每个成员节点的消息都放到r.msgs后,node.run会进入下一轮循环。此时:
1 | node.run |
发送到n.readyc通道中的消息,会被raftNode捕获:
1 | for { |
以上是Leader角色接收到Put请求后的处理逻辑,其中会向 Follower发送MsgApp消息。
Follower接收到Leader的广播,进行处理回复
当Follower收到消息后,会执行EtcdServer.Process方法:
1 | EtcdServer.Process |
发送的消息最终被 raftNode接收:
1 | for { |
再看EtcdServer接收到apply消息后的处理:
再来看 EtcdServer
1 | EtcdServer/1020L+- |
总结起来,Follower节点接收到Leader节点的MsgApp应用到其节点中,然后向反馈Leader MsgAppResp消息,并带上自己的最大记录号。
Leader收到Follower的回复进行处理
Leader收到MsgAppResp消息后,经过层层传递到stepLeader方法中:
1 | |- pr := r.getProgress(m.From) // 获取该节点的进度 |
我们先来看判断是否可以提交的逻辑:
1 | raft.maybeCommit |
r.bcastAppend()的处理过程之前以及介绍过,其会构造一遍Ready数据结构,带上CommitedEntries提交的数据、其他节点未同步的Entries,传递给raftNode。raftNode接收到此消息后,会执行与上面分析的Follower的逻辑类似。
线性一致性读
线性一致性读和一般的读不通的时候,会在进行真正的读请求之前,先与其他节点进行一个check,查看当前集群主是否发生了变化。通过etcdctl get ty命令,向监听本地2380端口的节点发送GRPC请求,会被中转到quotaKVServer上。get请求在etcdctl中会被转换成Range请求。quotaKVServer最终会调用EtcdServer.Range方法。
Leader节点收到请求后,广播心跳消息
1 | quotaKVServer |
线性一致性读的主要逻辑体现在 s.linearizableReadNotify中。
1 | s.linearizableReadNotify // 线性读通知 |
EtcdServer在启动的时候会启动linearizableReadLoop线性一致性读循环。
1 | linearizableReadLoop |
1 | node.ReadIndex |
Follower接收到Leader的心跳请求,并返回
Follower接收到Leader的心跳请求,最终会调用:
1 | r.raftLog.commitTo(m.Commit) |
Leader接收Follower回复,通知客户端请求继续
Leader节点接收到Follower的回复后,最终会调用如下逻辑:
1 | pr.RecentActive = true |
raftNode结构体接收到Ready消息时,会检查是否有ReadStates,然后对其进行处理:
1 | if len(rd.ReadStates) != 0 { |
再回到linearizableReadLoop中:
1 | select { |
到此就完成线性一致性读的全过程。