rpcを用いるとプログラミングの幅が広がります。
net/rpcパッケージ
Go言語に組み込まれているnet/rpcパッケージを使って、RPCサーバーを作成してみましょう。
1 2 3 4 |
mkdir rpcServer touch rpcServer/main.go mkdir rpcClient touch rpcClient/main.go |
まずは、サーバー側を書きます。
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 |
// rpcServer/main.go package main import ( "log" "net" "net/http" "net/rpc" "time" ) // Clientから受け取る引数 type Args struct{} // TimeServer type TimeServer int64 // replyは必ずポインタ func (t *TimeServer) GiveServerTime(args *Args, reply *int64) error { *reply = time.Now().Unix() return nil } func main() { // TimeServerをインスタンス化(newだとポインタが返る) timeServer := new(TimeServer) // TimeServerを登録 rpc.Register(timeServer) // DefaultServerに登録 rpc.HandleHTTP() l, e := net.Listen("tcp", ":4444") if e != nil { log.Fatal("listen error:", e) } fmt.Println("RPC Server Start...") http.Serve(l, nil) } |
続いて、Client側を作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package main import ( "log" "net/rpc" ) type Args struct{} func main() { var reply int64 args := Args{} client, err := rpc.DialHTTP("tcp", "localhost:4444") if err != nil { log.Fatal("Connect Error:", err) } err = client.Call("TimeServer.GiveServerTime", args, &reply) if err != nil { log.Fatal("Calling Error:", err) } log.Printf("%d", reply) } |
terminalを2つ立ち上げて実行してみましょう。
1 2 3 4 5 6 7 8 |
# terminal 1 $ go run rpcServer/main.go RPC Server Start... # terminal 2 $ go run rpcClient/main.go 2020/03/21 07:27:11 1584743231 |
Gorilla RPC
GorillaパッケージのRPCも試してみましょう。
1 |
go get github.com/gorilla/rpc |
以下のファイルを用意します。
1 2 3 |
mkdir jsonRPCServer touch jsonRPCServer/products.json touch jsonRPCServer/main.go |
products.jsonには、商品一覧を記述します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[ { "id": "1", "name": "Golang Cook Books", "author": "Selfnote" }, { "id":"2", "name": "Python Cook Books", "author": "Selfnote" } ] |
続いて、main.goに、JSONRPCServerを実装します。
これは、リクエストに応じて先ほど作成したJSONファイルからプロダクト情報を取得するサーバーになります。
なお、リクエストには、欲しい商品IDをキーとして渡してあげます。
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 |
package main import ( "encoding/json" "fmt" "io/ioutil" "log" "net/http" "os" "path/filepath" "github.com/gorilla/mux" "github.com/gorilla/rpc" gjson "github.com/gorilla/rpc/json" ) type Args struct { ID string } type Product struct { ID string `json:"id,omitempty"` Name string `json:"name,omitempty"` Author string `json:"author,omitempty"` } type JSONServer struct{} func (t *JSONServer) GiveProductDetail(r *http.Request, args *Args, reply *Product) error { var products []Product absPath, _ := filepath.Abs("jsonRPCServer/products.json") raw, readerr := ioutil.ReadFile(absPath) if readerr != nil { log.Println("error:", readerr) os.Exit(1) } marshalerr := json.Unmarshal(raw, &products) if marshalerr != nil { log.Println("error:", marshalerr) os.Exit(1) } for _, product := range products { if product.ID == args.ID { *reply = product break } } return nil } func main() { s := rpc.NewServer() // データのエンコードとでコードを双方向で行う s.RegisterCodec(gjson.NewCodec(), "application/json") s.RegisterService(new(JSONServer), "") r := mux.NewRouter() r.Handle("/rpc", s) fmt.Println("JSON RPC Sever Start...") http.ListenAndServe(":1234", r) } |
以下の方法で実行します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# terminal 1 go run main.go # terminal 2 curl -X POST \ http://localhost:1234/rpc \ -H 'cache-control: no-cache' \ -H 'content-type: application/json' \ -d '{ "method": "JSONServer.GiveProductDetail", "params": [{ "ID": "1" }], "id": "1" }' # 取得できた値 {"result":{"id":"1","name":"Golang Cook Books","author":"Selfnote"},"error":null,"id":"1"} |
結構便利そうですよね。
まとめ
ローカルだとRPCの便利さがあまり実感できないかもしれません。
これの更に発展系の技術として、gRPCがあります。
具体的には、以下の記事を参考にしてください。
コメントを残す
コメントを投稿するにはログインしてください。