こんにちは。KOUKIです。
前回は、GreetManyTimeAPIを実装しました。今回はその続きから実装します。
<目次>
前回
Go言語記事まとめ
Calc Stream API
前回の復習がてら、Calc Stream APIを作成しましょう。
まずは、calculator.protoファイルに次の定義を行います。
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 |
// calculator/calculatorpb/calculator.proto syntax = "proto3"; package calculator; option go_package="calculatorpb"; service CalculatorService { // Unary rpc Calc(CalcRequest) returns (CalcResponse) {}; // Server Streaming rpc CalcManyTimes(CalcManyTimesRequest) returns (stream CalcManyTimesResponse) {}; } message Number { int32 num1 = 1; int32 num2 = 2; } message CalcRequest { Number number = 1; } message CalcResponse { int32 sum = 1; } message CalcManyTimesRequest { int32 calcNum = 1; } message CalcManyTimesResponse { int32 result = 1; } |
下記のコマンドでコンパイルします。
1 |
./generate.sh |
次は、Serverコードを実装します。
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 |
// calculator/calculator_server/server.go package main import ( "context" "fmt" "log" "net" "time" "github.com/selfnote/golang-grpc/calculator/calculatorpb" "google.golang.org/grpc" ) type server struct{} func (*server) Calc(ctx context.Context, req *calculatorpb.CalcRequest) (*calculatorpb.CalcResponse, error) { fmt.Printf("Calc function was invoked with %v\n", req) num1 := req.GetNumber().GetNum1() num2 := req.GetNumber().GetNum2() sum := num1 + num2 res := &calculatorpb.CalcResponse{ Sum: sum, } return res, nil } // new func (*server) CalcManyTimes(req *calculatorpb.CalcManyTimesRequest, stream calculatorpb.CalculatorService_CalcManyTimesServer) error { fmt.Printf("CalcManyTimes function was invoked with %v\n", req) num := req.GetCalcNum() fmt.Println("num: ", num) var divNum int32 = 2 for num > 1 { if num%divNum == 0 { fmt.Println(divNum) stream.Send(&calculatorpb.CalcManyTimesResponse{ Result: num, }) num = num / divNum } else { divNum++ fmt.Printf("Divisor has incresaed to %v\n", divNum) } time.Sleep(1000 * time.Millisecond) } return nil } func main() { fmt.Println("Server Start...") lis, err := net.Listen("tcp", "0.0.0.0:50051") if err != nil { log.Fatalf("Failed to listen: %v", err) } s := grpc.NewServer() calculatorpb.RegisterCalculatorServiceServer(s, &server{}) if err := s.Serve(lis); err != nil { log.Fatalf("Failed to server %v", err) } } |
続いて、Clientコードを実装します。
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 |
// calculator/calculator_client/client.go package main import ( "context" "fmt" "io" "log" "github.com/selfnote/golang-grpc/calculator/calculatorpb" "google.golang.org/grpc" ) func doUnary(c calculatorpb.CalculatorServiceClient) { fmt.Println("Starting to do a Unary RPC...") req := &calculatorpb.CalcRequest { Number: &calculatorpb.Number{ Num1: 3, Num2: 10, }, } res, err := c.Calc(context.Background(), req) if err != nil { log.Fatalf("error while calling Calc RPC: %v", err) } log.Printf("Response from Calc: %v", res.Sum) } // new func doServerStreaming(c calculatorpb.CalculatorServiceClient) { fmt.Println("Starting to do a Server Streaming RPC...") req := &calculatorpb.CalcManyTimesRequest{ CalcNum: 120, } resStream, err := c.CalcManyTimes(context.Background(), req) if err != nil { log.Fatalf("error while calling CalcManyTimes RPC: %v", err) } for { msg, err := resStream.Recv() if err == io.EOF { break } if err != nil { log.Fatalf("error while reading stream: %v", err) } log.Printf("Response from CalcManyTimes: %v", msg.GetResult()) } } func main() { fmt.Println("Client Request...") cc, err := grpc.Dial("localhost:50051", grpc.WithInsecure()) if err != nil { log.Fatalf("Could not connect: %v", err) } defer cc.Close() c := calculatorpb.NewCalculatorServiceClient(cc) fmt.Println("Created client: %f\n", c) // doUnary(c) doServerStreaming(c) } |
下記のコマンドで、Server->Clientの順でプログラムを実行しましょう。
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 |
make c_srv go run calculator/calculator_server/server.go Server Start... CalcManyTimes function was invoked with calcNum:120 num: 120 2 2 2 Divisor has incresaed to 3 3 Divisor has incresaed to 4 Divisor has incresaed to 5 5 make c_cnt go run calculator/calculator_client/client.go Client Request... Created client: %f &{0xc0000f6700} Starting to do a Server Streaming RPC... 2019/10/25 14:51:57 Response from CalcManyTimes: 120 2019/10/25 14:51:58 Response from CalcManyTimes: 60 2019/10/25 14:51:59 Response from CalcManyTimes: 30 2019/10/25 14:52:01 Response from CalcManyTimes: 15 2019/10/25 14:52:04 Response from CalcManyTimes: 5 |
問題なく実行されましたね。復習終わりです。
LongGreetAPI – プロトファイルの作成 –
本日の目玉は、Client Streaming APIです。
Client Streaming APIには、以下の特徴があります。
・ クライアントから大量のデータを送りたいときに適する
・ サーバーの処理負荷が高く、クライアントからデータを送信したいときに適する
・ サーバーのレスポンスを待たないで、クライアントからデータを送信したいときに適する
Client Streaming APIを使用するときは、protoファイルのserviceのrequestを”stream“keywordと共に宣言します。
protoファイルを作成します。
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 |
// greet/greetpb/greet.proto // version syntax = "proto3"; // go pakcage package greet; // Include subdirectory into package too option go_package="greetpb"; service GreetService{ // Unary rpc Greet(GreetRequest) returns (GreetResponse){}; // Server Streaming rpc GreetManyTimes(GreetManyTimesRequest) returns (stream GreetManyTimesResponse){}; // new // Client Streaming rpc LongGreet(stream LongGreetRequest) returns (LongGreetResponse) {}; }; message Greeting { string first_name = 1; string last_name = 2; } message GreetRequest { Greeting greeting = 1; } message GreetResponse { string result = 1; } message GreetManyTimesRequest { Greeting greeting = 1; } message GreetManyTimesResponse { string result = 1; } // new message LongGreetRequest { Greeting greeting = 1; } // new message LongGreetResponse { string result = 1; } |
protoファイルが定義できたら、以下のコマンドでコンパイルします
1 |
./generate.sh |
LongGreetAPI – Serverコードの作成 –
LongGreet関数を実装します。
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 |
// greet/greet_server/server.go package main import ( "context" "fmt" "io" "log" "net" "strconv" "time" "github.com/selfnote/golang-grpc/greet/greetpb" "google.golang.org/grpc" ) type server struct{} func (*server) Greet(ctx context.Context, req *greetpb.GreetRequest)(*greetpb.GreetResponse, error) { fmt.Printf("Greet function was invoked with %v\n", req) firstName := req.GetGreeting().GetFirstName() result := "Hello " + firstName res := &greetpb.GreetResponse { Result: result, } return res, nil } func (*server) GreetManyTimes(req *greetpb.GreetManyTimesRequest, stream greetpb.GreetService_GreetManyTimesServer) error { fmt.Printf("GreetManyTimes function was invoked with %v\n", req) firstName := req.GetGreeting().GetFirstName() // Do print 10 times. for i := 0; i < 10; i++ { result := "Hello " + firstName + " number" + strconv.Itoa(i) res := &greetpb.GreetManyTimesResponse { Result: result, } stream.Send(res) time.Sleep(1000 * time.Millisecond) } return nil } // new func (*server) LongGreet(stream greetpb.GreetService_LongGreetServer) error { fmt.Println("LongGreet function was invoked with a streaming request") result := "" for { req, err := stream.Recv() if err == io.EOF { return stream.SendAndClose(&greetpb.LongGreetResponse{ Result: result, }) } if err != nil { log.Fatalf("Error while reading client stream: %v", err) } firstName := req.GetGreeting().GetFirstName() result += "Hello " + firstName + "! " } } func main() { fmt.Println("Hello World") lis, err := net.Listen("tcp", "0.0.0.0:50051") if err != nil { log.Fatalf("Failed to listen: %v", err) } s := grpc.NewServer() greetpb.RegisterGreetServiceServer(s, &server{}) if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } } |
LongGreetAPI – Clientコードの作成 –
続いて、Client側のコードを実装していきます。
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 111 112 113 114 115 116 117 118 119 120 |
// greet/greet_client/client.go package main import ( "context" "fmt" "io" "log" "time" "github.com/selfnote/golang-grpc/greet/greetpb" "google.golang.org/grpc" ) func main() { fmt.Println("Hello I'm a client") cc, err := grpc.Dial("localhost:50051", grpc.WithInsecure()) if err != nil { log.Fatalf("could not connect: %v", err) } defer cc.Close() c := greetpb.NewGreetServiceClient(cc) // doUnary(c) // doServerStreaming(c) doClientStreaming(c) } func doUnary(c greetpb.GreetServiceClient) { fmt.Println("Starting to do a Unary RPC...") req := &greetpb.GreetRequest{ Greeting: &greetpb.Greeting{ FirstName: "Harry", LastName: "Potter", }, } res, err := c.Greet(context.Background(), req) if err != nil { log.Fatalf("error whiole caling Greet RPC: %v\n", err) } log.Printf("Response from Greet: %v\n", res.Result) } func doServerStreaming(c greetpb.GreetServiceClient) { fmt.Println("Starting to do a Server Streaming PRC...") req := &greetpb.GreetManyTimesRequest { Greeting: &greetpb.Greeting { FirstName: "Hary", LastName: "Potter", }, } resStream, err := c.GreetManyTimes(context.Background(), req) if err != nil { log.Fatalf("error while calling GreetManyTimes RPC: %v", err) } for { msg, err := resStream.Recv() if err == io.EOF { fmt.Println(io.EOF) break } if err != nil { log.Fatalf("error while reading stream: %v", err) } log.Printf("Response from greetManyTimes: %v", msg.GetResult()) } } // new func doClientStreaming(c greetpb.GreetServiceClient) { fmt.Println("Streaming to do a Cient streaming RPC...") requests := []*greetpb.LongGreetRequest { &greetpb.LongGreetRequest { Greeting: &greetpb.Greeting { FirstName: "Harry", }, }, &greetpb.LongGreetRequest { Greeting: &greetpb.Greeting { FirstName: "Mark", }, }, &greetpb.LongGreetRequest { Greeting: &greetpb.Greeting { FirstName: "Mary", }, }, &greetpb.LongGreetRequest { Greeting: &greetpb.Greeting { FirstName: "Jane", }, }, &greetpb.LongGreetRequest { Greeting: &greetpb.Greeting { FirstName: "Upper", }, }, } stream, err := c.LongGreet(context.Background()) if err != nil { log.Fatalf("error while calling LongGreet: %v", err) } for _, req := range requests { fmt.Println("Sending req: %v\n", req) stream.Send(req) time.Sleep(1000 * time.Millisecond) } res, err := stream.CloseAndRecv() if err != nil { log.Fatalf("error while receiving response from LongGreet: %v\n", err) } fmt.Printf("LongGreet Response: %v\n", res) } |
Clientからは複数のデータを送りたいので、requestsにデータを5つほど詰め込んで、Server側へコードを送信する処理を書きました。
LongGreetAPI – 動作確認 –
Server -> Clientの順に下記のコマンドでプログラムを実行します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
make server go run greet/greet_server/server.go Hello World LongGreet function was invoked with a streaming request make client go run greet/greet_client/client.go Hello I'm a client Streaming to do a Cient streaming RPC... Sending req: %v greeting:<first_name:"Harry" > Sending req: %v greeting:<first_name:"Mark" > Sending req: %v greeting:<first_name:"Mary" > Sending req: %v greeting:<first_name:"Jane" > Sending req: %v greeting:<first_name:"Upper" > LongGreet Response: result:"Hello Harry! Hello Mark! Hello Mary! Hello Jane! Hello Upper! " |
次回
次回は、Bi Directional Streaming APIについて学習します。
コメントを残す
コメントを投稿するにはログインしてください。