2020 6.824 的 Raft Lab 3A

前言

做2020的MIT6.824,完成了实验Raft Lab3A,通过了测试,对于之前的Raft实现的实验请参考Raft Lab 2ARaft Lab 2B 以及 Raft Lab 2C

Lab3A主要是实现应用层的数据储存,通过之前已经实现Raft的帮助,实现strong consistent. 具体来说客户端Clerk给kvserver发送Get/Put/Append的请求,kvserver associate Raft的一个peer. 实验重点实现key/value service,对应就是server.go以及client.go。


一、Key/value Service

1.1 流程图

大概流程就是

  1. client给kvserver发送请求,一共有两个API,分别是Get和PutAppend
  2. kvserver收到请求,向Raft发送Start,并等待Raft发送过来的applyCh(如果此时Raft peer已经不是leader,返回error)
  3. 收到applyCh后,处理applyCh返回的Command的请求,比如是Put则把kv存到kvserver的map中,最后通过chan通知第2步等待的server
  4. 收到第3步的channel后处理对应的信息,并返回client

当然,这是个非常简化的流程图,中间涉及很多细节后面会讨论,Get跟PutAppend流程类似,这里就画了一张PutAppend的超简流程图先宏观上了解一下


在这里插入图片描述



二、client

1.1 arguments

在common.go中,添加了两个属性,sequenceId跟clientId,这主要为了在parallel中实现Linearizable,也就是保证每个operation是按顺序执行

You will need to uniquely identify client operations to ensure that the key/value service executes each one just once.

type PutAppendArgs struct {
   
   
	Key   string
	Value string
	Op    string // "Put" or "Append"
	// You'll have to add definitions here.
	// Field names must start with capital letters,
	// otherwise RPC will break.
	SequenceId int64
	ClientId   int64
}

type GetArgs struct {
   
   
	Key string
	// You'll have to add definitions here.
	SequenceId int64
	ClientId   int64
}

1.2 client属性

按照提示,在client中,同时需要记录leaderId

You will probably have to modify your Clerk to remember which server turned out to be the leader for the last RPC, and send the next RPC to that server first. This will avoid wasting time searching for the leader on every RPC, which may help you pass some of the tests quickly enough.

type Clerk struct {
   
   
	servers []*labrpc.ClientEnd
	// You will have to modify this struct.
	mu         sync.Mutex
	leaderId   int
	clientId   int64 
	sequenceId int64
}

1.3 client的Get/PutAppend请求

就是给server发请求,如果leader不是client.leader则leaderId++重新请求,如果 reply.Err == OK则为成功,同时return

func (ck *Clerk) Get(key string) string {
   
   
	// You will have to modify this function.
	args := GetArgs{
   
   Key: key, ClientId: ck.clientId, SequenceId: atomic.AddInt64(&ck.sequenceId, 1)}
	leaderId := ck.currentLeader()
	for {
   
   
		reply := GetReply{
   
   }
		if ck.servers[leaderId].Call("KVServer.Get", &args, &reply) {
   
   
			if reply.Err == OK {
   
   
				return reply.Value
			} else if reply.Err == ErrNoKey {
   
   
				return ""
			}
		}
		leaderId = ck.changeLeader()
		time.Sleep(1 * time.Millisecond)
	}
}

func 
### 6.824 Raft 实验测试脚本下载与示例 针对6.824课程中的Raft实验,通常会使用Go语言编写测试脚本来验证实现的正确性和稳定性。这些测试脚本位于项目的`tests`目录下,并且可以通过执行特定命令来运行。 #### 获取项目源码及其测试套件 为了获得完整的测试环境,建议克隆官方GitHub仓库: ```bash git clone https://github.com/username/lab.git cd lab/src/raft ``` 这里假设`username`代表维护该实验室代码的具体账户名,在实际操作时应替换为正确的用户名或组织名称。 #### 运行基本测试案例 一旦获得了源码库,就可以利用内置工具来进行初步的功能性检测: ```bash go test -run 2A ``` 上述命令专门用于检验Leader Election部分是否正常工作[^3]。当所有指定条件满足后,终端将会显示出相应的成功消息以及耗时统计信息。 #### 调试复杂场景下的行为表现 对于更深入的问题排查或是性能瓶颈分析,则需依赖于定制化的shell辅助程序——例如提到过的`test-mr.sh`。此脚本负责在子目录`mr-tmp`内启动一系列流程;遇到异常状况时能够暂停后续动作以便审查中间产物[^1]。 此外,面对偶发性的故障现象(比如每几十次甚至上百次才重现一次的情况),应当考虑增强日志记录强度,即编辑配置文件`config.go`加入更多诊断语句帮助定位潜在缺陷所在位置[^2]。 #### 示例:调整Shell Script以适应不同需求 下面给出了一段经过简单改造后的`test-mr.sh`片段,旨在展示如何设置断点从而便于观察具体环节的状态变化: ```bash #!/bin/bash set -e # 遇到任何错误立即终止整个脚本执行 # 原始逻辑... ./your_test_program || exit $? # 如果测试失败则停止进一步的操作 echo "Test failed, inspecting intermediate files..." ls mr-p "Press enter to continue or Ctrl+C to abort..." # 提供人工干预机会 ``` 通过这种方式可以在每次出现问题之后手动决定下一步行动方向,而不会因为自动清理机制而导致有价值线索丢失。
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值