Use RPC to make a kv storage server
Go example: kv.go on schedule page
 A toy key/value storage server – Put(key,value), Get(key)->value
 Uses Go’s RPC library
 Common:
 Declare Args and Reply struct for each server handler.
 Client:
 connect()'s Dial() creates a TCP connection to the server
 get() and put() are client “stubs”
 Call() asks the RPC library to perform the call
 you specify server function name, arguments, place to put reply
 library marshalls args, sends request, waits, unmarshalls reply
 return value from Call() indicates whether it got a reply
 usually you’ll also have a reply.Err indicating service-level failure
 Server:
 Go requires server to declare an object with methods as RPC handlers
 Server then registers that object with the RPC library
 Server accepts TCP connections, gives them to RPC library
 The RPC library
 reads each request
 creates a new goroutine for this request
 unmarshalls request
 looks up the named object (in table create by Register())
 calls the object’s named method (dispatch)
 marshalls reply
 writes reply on TCP connection
 The server’s Get() and Put() handlers
 Must lock, since RPC library creates a new goroutine for each request
 read args; modify reply
server
//
// Server
//package mainimport ("log""net""net/rpc""sync"
)const (OK       = "OK"ErrNoKey = "ErrNoKey"
)type Err stringtype PutArgs struct {Key   stringValue string}type PutReply struct {Err Err        
}type GetArgs struct {Key string
}type GetReply struct {Err   ErrValue string     
}type KV struct {mu   sync.Mutexdata map[string]string
}func server() {kv := new(KV)kv.data = map[string]string{}rpcs := rpc.NewServer()rpcs.Register(kv)l, e := net.Listen("tcp", ":3306")if e != nil {log.Fatal("listen error:", e)}go func() {for {conn, err := l.Accept()if err == nil {go rpcs.ServeConn(conn)} else {break}}l.Close()}()
}func (kv *KV) Get(args *GetArgs, reply *GetReply) error {kv.mu.Lock()defer kv.mu.Unlock()val, ok := kv.data[args.Key]if ok {reply.Err = OKreply.Value = val} else {reply.Err = ErrNoKeyreply.Value = ""}return nil
}func (kv *KV) Put(args *PutArgs, reply *PutReply) error {kv.mu.Lock()defer kv.mu.Unlock()kv.data[args.Key] = args.Valuereply.Err = OKreturn nil
}//
// main
//func main() {server()time.Sleep(time.Second * 10)
}
client
package mainimport ("fmt""log""net/rpc"
)//
// Common RPC request/reply definitions
//const (OK       = "OK"ErrNoKey = "ErrNoKey"
)type Err stringtype PutArgs struct {Key   stringValue string
}type PutReply struct {Err Err
}type GetArgs struct {Key string
}type GetReply struct {Err   ErrValue string
}//
// Client
//func connect() *rpc.Client {client, err := rpc.Dial("tcp", ":3306")if err != nil {log.Fatal("dialing:", err)}return client
}func get(key string) string {client := connect()args := GetArgs{"subject"}reply := GetReply{}err := client.Call("KV.Get", &args, &reply)if err != nil {log.Fatal("error:", err)}client.Close()return reply.Value
}func put(key string, val string) {client := connect()args := PutArgs{"subject", "6.824"}reply := PutReply{}err := client.Call("KV.Put", &args, &reply)if err != nil {log.Fatal("error:", err)}client.Close()
}func main() {put("subject", "6.824")fmt.Printf("Put(subject, 6.824) done\n")fmt.Printf("get(subject) -> %s\n", get("subject"))
}
Result
Put(subject, 6.824) done
get(subject) -> 6.824