使用RPC消息传递可以实现分布式系统的设计开发。
什么是RPC:
1 2 3 4 |
1- The method is exported. 2- The method has two arguments, both exported (or builtin) types. 3- The method's second argument is a pointer. 4- The method has return type error. |
定义一个Struct:
1 2 3 4 5 6 7 |
//Represents Arith service for RPC type Arith int //Arith service has procedure Multiply which takes numbers A, B as arguments and returns error or stores product in reply func (t *Arith) Multiply(args *Args, reply *int) error { *reply = args.A * args.B return nil } |
我们需要定义一个参数类型结构拥有参数传递给方法:
1 2 3 |
type Args struct { A, B int } |
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 |
package rpcexample import ( "log" ) //Holds arguments to be passed to service Arith in RPC call type Args struct { A, B int } //Representss service Arith with method Multiply type Arith int //Result of RPC call is of this type type Result int //This procedure is invoked by rpc and calls rpcexample.Multiply which stores product of args.A and args.B in result pointer func (t *Arith) Multiply(args Args, result *Result) error { return Multiply(args, result) } //stores product of args.A and args.B in result pointer func Multiply(args Args, result *Result) error { log.Printf("Multiplying %d with %d\n", args.A, args.B) *result = Result(args.A * args.B) return nil } |
RPC server using Golang net/rpc package:
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 |
package main import ( "github.com/haisum/rpcexample" "log" "net" "net/http" "net/rpc" ) func main() { //register Arith object as a service arith := new(rpcexample.Arith) err := rpc.Register(arith) if err != nil { log.Fatalf("Format of service Arith isn't correct. %s", err) } rpc.HandleHTTP() //start listening for messages on port 1234 l, e := net.Listen("tcp", ":1234") if e != nil { log.Fatalf("Couldn't start listening on port 1234. Error %s", e) } log.Println("Serving RPC handler") err = http.Serve(l, nil) if err != nil { log.Fatalf("Error serving: %s", err) } } |
RPC client using Golang net/rpc
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 |
package main import ( "github.com/haisum/rpcexample" "log" "net/rpc" ) func main() { //make connection to rpc server client, err := rpc.DialHTTP("tcp", ":1234") if err != nil { log.Fatalf("Error in dialing. %s", err) } //make arguments object args := &rpcexample.Args{ A: 2, B: 3, } //this will store returned result var result rpcexample.Result //call remote procedure with args err = client.Call("Arith.Multiply", args, &result) if err != nil { log.Fatalf("error in Arith", err) } //we got our result in result log.Printf("%d*%d=%d\n", args.A, args.B, result) } |
JSON RPC server using gorilla rpc/json
Gorilla kit has rpc package to simplify default net/rpc/jsonrpc package. Slight difference form standard golang net/rpc
is that it requires method signature to accept *Request object as first argument and changes Args parameter to pointer *Args.
In net/rpc
our Multiply method looks like func (t *Arith) Multiply(args Args, result *Result) error
. For gorilla it should look like func (t *Arith) Multiply(r *http.Request, args *Args, result *Result) error
.
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 |
package main import ( "github.com/gorilla/mux" "github.com/gorilla/rpc" "github.com/gorilla/rpc/json" "log" "net/http" ) type Args struct { A, B int } type Arith int type Result int func (t *Arith) Multiply(r *http.Request, args *Args, result *Result) error { log.Printf("Multiplying %d with %d\n", args.A, args.B) *result = Result(args.A * args.B) return nil } func main() { s := rpc.NewServer() s.RegisterCodec(json.NewCodec(), "application/json") s.RegisterCodec(json.NewCodec(), "application/json;charset=UTF-8") arith := new(Arith) s.RegisterService(arith, "") r := mux.NewRouter() r.Handle("/rpc", s) http.ListenAndServe(":1234", r) } |
JSON RPC client using gorilla rpc/json
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 |
package main import ( "bytes" "github.com/gorilla/rpc/json" "github.com/haisum/rpcexample" "log" "net/http" ) func main() { url := "http://localhost:1234/rpc" args := &rpcexample.Args{ A: 2, B: 3, } message, err := json.EncodeClientRequest("Arith.Multiply", args) if err != nil { log.Fatalf("%s", err) } req, err := http.NewRequest("POST", url, bytes.NewBuffer(message)) if err != nil { log.Fatalf("%s", err) } req.Header.Set("Content-Type", "application/json") client := new(http.Client) resp, err := client.Do(req) if err != nil { log.Fatalf("Error in sending request to %s. %s", url, err) } defer resp.Body.Close() var result rpcexample.Result err = json.DecodeClientResponse(resp.Body, &result) if err != nil { log.Fatalf("Couldn't decode response. %s", err) } log.Printf("%d*%d=%d\n", args.A, args.B, result) } |
有疑问加站长微信联系(非本文作者)