Go RPC

RPC(Remote Poresedure Call)是远程方法调用的缩写。Go的RPC库可以实现通过网络或者其他I/O方式远程调用对象的方法。

服务器注册一个对象,让它作为一个以对象类型命名的服务,让这个对象导出的方法可以被远程调用。一个服务器可以注册多个不同类型的对象,但是不能注册同一类型的多个对象。

一个能够被远程调用的方法应该像下面这样:

1
func (t *T) MethodName(argType T1, replyType *T2) error

一个简单的例子:实现简单的kv存储,并(在同一台机器上)通过RPC调用Put和Get方法。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
package main

import (
    "fmt"
    "log"
    "net"
    "net/http"
    "net/rpc"
    "sync"
    "time"
)

// RPC struct definition

type PutArgs struct {
    Key string
    Val string
}

type PutReply struct {
    Ok bool
}

type GetArgs struct {
    Key string
}

type GetReply struct {
    Val string
}

type Kv struct {
    data map[string]string
    mu   sync.Mutex
}

// server

func (kv *Kv) Get(args *GetArgs, reply *GetReply) error {
    kv.mu.Lock()
    defer kv.mu.Unlock()
    reply.Val = kv.data[args.Key]
    return nil
}

func (kv *Kv) Put(args *PutArgs, reply *PutReply) error {
    kv.mu.Lock()
    defer kv.mu.Unlock()
    kv.data[args.Key] = args.Val
    reply.Ok = true
    return nil
}

func KvServer() {
    kv := new(Kv)
    kv.data = make(map[string]string)
    rpc.Register(kv)
    rpc.HandleHTTP()
    l, err := net.Listen("tcp", ":1234")
    if err != nil {
        log.Fatal("listen error: ", err)
    }
    http.Serve(l, nil)
}

// client interface

func get(key string) string {
    client, err := rpc.DialHTTP("tcp", "127.0.0.1"+":1234")
    if err != nil {
        log.Fatal("dialing:", err)
    }
    args := GetArgs{key}
    reply := GetReply{}
    e := client.Call("Kv.Get", args, &reply)
    if e != nil {
        log.Fatal("get error", e)
    }
    client.Close()
    return reply.Val
}

func put(key string, val string) bool {
    client, err := rpc.DialHTTP("tcp", "127.0.0.1"+":1234")
    if err != nil {
        log.Fatal("dialing:", err)
    }
    args := PutArgs{key, val}
    reply := PutReply{}
    e := client.Call("Kv.Put", args, &reply)
    if e != nil {
        log.Fatal("put error", e)
    }
    client.Close()
    return reply.Ok
}

// test

func main() {
    go KvServer()
    time.Sleep(time.Second) // wait for server to start
    fmt.Println(put("key1", "value1"))
    fmt.Println(put("key2", "value2"))
    fmt.Println(put("key3", "value3"))
    fmt.Println(get("key1"))
    fmt.Println(get("key2"))
    fmt.Println(get("key3"))
    fmt.Println(get("value1"))
}
Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy