MIT6.5840 2024 Spring Lab2
前言
这次实验是五个实验里应该是最简单的了,这里就不多废话了,需要的知识基本和前面一样,直接根据实验要求扔思路吧。
思路
实验要求实现能让客户端远程调用的Put()、Get()和Append(),其中Put给定key和value往服务器添加新的数据,Get根据key从服务器获取value,Append给定key和value请求服务器往key对应的value上追加字段,如果key不存在默认该key对应的字符为空字符。
多个客户端发起请求,同一时刻只能有一个请求被处理,需要互斥访问临界资源,并且实验测试文件会模拟网络不好的情景,同一个请求可能会发送多次,所以需要唯一标识每个请求,并且本地临时存储上次请求的结果,测试文件还会检测内存使用情况,不需要的请求结果应该及时删掉,这就需要客户端收到回复后再给服务器回应确认消息。
你可以假设每个客户端只对服务器发起一次请求,其实只需要唯一标识消息ID即可,客户端不用唯一标识,但我实现的代码也唯一标识了客户端,我是写完之后才看到这个提示,所以就默认了同一个客户端可能会多次发起请求,并且请求可能会重复。
代码实现
RPC通信消息格式
- //Put Append请求
- type PutAppendArgs struct {
- Key string
- Value string
- Clientid int64
- Requestid int64
- ReceiveFlag bool //True为回复消息
- }
- //Put Append回复
- type PutAppendReply struct {
- Value string
- }
- //Get请求
- type GetArgs struct {
- Key string
- Clientid int64
- Requestid int64
- ReceiveFlag bool
- }
- //Get回复
- type GetReply struct {
- Value string
- }
复制代码 Client
- type Clerk struct {
- server *labrpc.ClientEnd
- clientid int64 //增加
- }
- func MakeClerk(server *labrpc.ClientEnd) *Clerk {
- ck := new(Clerk)
- ck.server = server
- ck.clientid = nrand()//随机产生ID号
- return ck
- }
- func (ck *Clerk) Get(key string) string {
- id := nrand() //消息ID随机产生
- request := GetArgs{key,ck.clientid,id,false}
- response := GetReply{""}
- ok := false
- for !ok {
- ok = ck.server.Call("KVServer.Get",&request,&response)
- }
- request.ReceiveFlag = true
- ok = false
- for !ok { //回复服务器已经接受到结果
- ok = ck.server.Call("KVServer.Get",&request,&response)
- }
- return response.Value
- }
- //和Get结构一样,只是改了下调用函数
- func (ck *Clerk) PutAppend(key string, value string, op string) string {
- id := nrand()
- request := PutAppendArgs{key,value,ck.clientid,id,false}
- response := PutAppendReply{""}
- ok := false
- for !ok {
- ok = ck.server.Call("KVServer."+op,&request,&response)
- }
- request.ReceiveFlag = true
- ok = false
- for !ok {
- ok = ck.server.Call("KVServer."+op,&request,&response)
- }
- return response.Value
- }
复制代码 Server
- type KVServer struct {
- mu sync.Mutex
- keyvalue map[string]string //存储键值对
- ClientLastRequest map[int64]struct{//记录还没收到回复的请求
- Requestid int64
- ReplyMsg string
- }
- }
- func (kv *KVServer) Get(args *GetArgs, reply *GetReply) {
- kv.mu.Lock()
- defer kv.mu.Unlock()
- if args.ReceiveFlag {//此消息是回复消息
- delete(kv.ClientLastRequest,args.Clientid) //删除记录
- return
- }
- value,ok := kv.ClientLastRequest[args.Clientid]
- if ok && value.Requestid == args.Requestid { //判断是否为重复请求,如果是重复请求直接返回上次的结果
- reply.Value = value.ReplyMsg
- return
- }
- reply.Value = kv.keyvalue[args.Key]
- kv.ClientLastRequest[args.Clientid] = struct{Requestid int64; ReplyMsg string}{args.Requestid,reply.Value}
-
- }
- func (kv *KVServer) Put(args *PutAppendArgs, reply *PutAppendReply) {
- kv.mu.Lock()
- defer kv.mu.Unlock()
- if args.ReceiveFlag {
- delete(kv.ClientLastRequest,args.Clientid)
- return
- }
- value,ok := kv.ClientLastRequest[args.Clientid]
- if ok && value.Requestid == args.Requestid {
- reply.Value = value.ReplyMsg
- return
- }
- kv.keyvalue[args.Key] = args.Value
- kv.ClientLastRequest[args.Clientid] = struct{Requestid int64; ReplyMsg string}{args.Requestid,""}
-
- }
- func (kv *KVServer) Append(args *PutAppendArgs, reply *PutAppendReply) {
- kv.mu.Lock()
- defer kv.mu.Unlock()
- if args.ReceiveFlag {
- delete(kv.ClientLastRequest,args.Clientid)
- return
- }
- value,ok := kv.ClientLastRequest[args.Clientid]
- if ok && value.Requestid == args.Requestid {
- reply.Value = value.ReplyMsg
- return
- }
- oldvalue := kv.keyvalue[args.Key]
- kv.keyvalue[args.Key] = oldvalue + args.Value
- reply.Value = oldvalue
- kv.ClientLastRequest[args.Clientid] = struct{Requestid int64; ReplyMsg string}{args.Requestid,oldvalue}
-
- }
- func StartKVServer() *KVServer {
- kv := new(KVServer)
-
- //初始化
- kv.keyvalue = make(map[string]string)
- kv.ClientLastRequest =make(map[int64]struct{Requestid int64; ReplyMsg string})
- return kv
- }
复制代码 资料
MIT6.5840 Lab2主页
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |