OceanPresent

MIT-6.5840 Lab2笔记


任务目标

Lab 2要求我们实现一个和K-V Server。字面意思就是一个键值对存储服务,键值对存储在服务器的内存里。

用于测试的文件在src/kvsrv/test_test.go,运行命令go testgo test -race进行测试。命令go test -v -run ${func_name}对指定测试样例进行测试,也可以在VSCode中进入文件点击Test按钮

前置知识

文档资料

以下列出完成Lab 1所需要阅读的资料:

  • Lab 2 2024文档 介绍了Lab2的任务和一些规范,同时给出了一些实现提示
  • MIT 6.824 6.824的官方视频课程,完成Lab 2只需要看第五节甚至不需要看
  • Go语言教程 对于有一定编程基础,要快速入门书写Go程序的。这本国内的教程足够,既涵盖了基本语法,也提供了一些常用标准库的API供随时查看

课程程序规范

Lab2主要关注如下文件

  • src/kvsrv/client.go 客户端,负责进行RPC调用,只需根据Lab文档进行略微修改
  • src/kvsrv/server.go KV Server的定义,需要在这里完成各操作的具体实现
  • src/kvsrv/common.go 一些公用的类型定义
  • src/kvsrv/test_test.go 测试文件,一般不需要动。如果某个测试样例过不去可以看该样例的函数,了解该样例大概需要完成什么任务,有助于Debug

实现思路

数据结构

Clerk

type Clerk struct {
	server *labrpc.ClientEnd
	// You will have to modify this struct.
	instructionCounter InstructionId
	clientId           ClientId
}

Clerk就是客户端,server已经为我们提供,用于发送RPC请求

ClientId是客户端的唯一标识,由一个全局变量ClientCounter计数,由于Lab中Client不会并发创建,就不需要加锁

instructionCounter是本客户端的指令计数器,为该客户端的指令提供唯一标识,由于Lab中Client不会并发发送RPC请求(见Task2的Hint),就不需要加锁

CacheData

type CacheData struct {
    ClientId      ClientId
	InstructionId InstructionId
	value         string
}

Task2要求我们对Client的重复请求进行过滤,因此我们需要做一个类似日志/缓存的队列保存过去的请求和请求结果,确保一致性。我使用ClientIdInstructionId来作为请求的唯一标识,同时记录请求的返回值。ClientIdInstructionId两个类型是Int16的Alias。

KV-Server

type KVServer struct {
	mu sync.Mutex

	// Your definitions here.
	kvState  map[string]string
	InsCache map[ClientId][]CacheData
}

Lab要求我们在内存中保存KV Pairs,显然使用map是最合适不过。对于缓存我为每个Client创建了一个缓存队列,保存该Client的请求缓存。写到一半发现其实完全没必要做缓存队列,因为本Lab中其实每个Client只会有一个请求缓存,因为Client在请求失败后会阻塞不断地尝试同一个请求,此请求前的其他请求对它已经过时了没有用(见Task2)。这里可以修改成map[ClientId]CacheData

KV-Server

实现起来其实没有什么太明显的难点,这里把要注意的地方列出来,相信可以解决读者的瓶颈:

  • Append函数要返回的是操作前的Value值而不是append后的新值
  • KV-Server的状态在读写时记得加锁,由于Cache和State是紧密同步的,要把他们的分布更新作为一个原子操作。即放在同一段临界区
  • Clerk的每个操作都用for循环包裹直至成功

This website has been running for 3 years 2 months 12 days 15 hours 28 minutes 45 seconds

皖ICP备2021007094号 2021-PRESENT © OceanPresent