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 { |
到此就完成线性一致性读的全过程。