一、獲取gRPCgit
環境變量GOPATH的src目錄下執行:github
git clone https://github.com/grpc/grpc-go.git google.golang.org/grpcgolang
git clone https://github.com/golang/net.git golang.org/x/nettcp
git clone https://github.com/golang/text.git golang.org/x/textgoogle
go get -u github.com/golang/protobuf/protoc-gen-gospa
git clone https://github.com/google/go-genproto.git google.golang.org/genprotoblog
go install google.golang.org/grpcrpc
二、proto文件get
gRPC 容許定義4種類型的 service 方法。string
(1)編寫test.proto
syntax = "proto3"; package test; //參數 message Question{ string question_str = 1; } //返回 message Answer{ string answer_str = 1; } //定義服務 service Test{ //簡單RPC rpc GetAnswer1(Question) returns (Answer){} //服務端流式RPC rpc GetAnswer2(Question) returns (stream Answer){} //客戶端流式RPC rpc GetAnswer3(stream Question) returns (Answer){} //雙向流式RPC rpc GetAnswer4(stream Question) returns (stream Answer){} }
(2)生成文件test.pb.go
protoc --go_out=plugins=grpc:. test.proto
三、服務端
package main import ( "context" "fmt" "io" "log" "net" "test/grpc/test" "google.golang.org/grpc" ) type testServer struct{} //簡單RPC //客戶端一次請求,服務端一次響應 func (*testServer) GetAnswer1(ctx context.Context, q *test.Question) (*test.Answer, error) { answer := test.Answer{AnswerStr: fmt.Sprintf("Question:%s;Answer:%s。", q.QuestionStr, "Answer1")} return &answer, nil } //服務端流式RPC //客戶端一次請求,服務端屢次響應 func (*testServer) GetAnswer2(q *test.Question, stream test.Test_GetAnswer2Server) error { for i := 1; i <= 3; i++ { answer := test.Answer{AnswerStr: fmt.Sprintf("Question:%s;Answer:%s%d。", q.QuestionStr, "Answer", i)} if err := stream.Send(&answer); err != nil { return err } } return nil } //客戶端流式RPC //客戶端屢次請求,服務端一次響應 func (*testServer) GetAnswer3(stream test.Test_GetAnswer3Server) error { answer := test.Answer{} for i := 1; ; i++ { question, err := stream.Recv() if err == io.EOF { return stream.SendAndClose(&answer) } if err != nil { return err } answer.AnswerStr = fmt.Sprintf("%sQuestion:%s;Answer:Answer%d。\n", answer.AnswerStr, question.QuestionStr, i) } } //雙向流式RPC //客戶端屢次請求,服務端屢次響應 func (*testServer) GetAnswer4(stream test.Test_GetAnswer4Server) error { for i := 1; ; i++ { question, err := stream.Recv() if err == io.EOF { return nil } if err != nil { return err } answer := test.Answer{AnswerStr: fmt.Sprintf("Question:%s;Answer:%s%d。", question.QuestionStr, "Answer", i)} if err = stream.Send(&answer); err != nil { return err } } } func main() { lis, err := net.Listen("tcp", "127.0.0.1:5000") if err != nil { log.Fatalf("failed to listen: %v", err) } grpcServer := grpc.NewServer() test.RegisterTestServer(grpcServer, &testServer{}) grpcServer.Serve(lis) }
四、客戶端
package main import ( "context" "fmt" "io" "log" "test/grpc/test" "google.golang.org/grpc" ) func main() { conn, err := grpc.Dial("127.0.0.1:5000", grpc.WithInsecure()) if err != nil { log.Fatalf("fail to dial: %v", err) } defer conn.Close() client := test.NewTestClient(conn) fmt.Println("簡單RPC===========================") question := test.Question{QuestionStr: "問題11111111?"} answer, err := client.GetAnswer1(context.Background(), &question) if err != nil { log.Fatalf("fail to GetAnswer1: %v", err) } fmt.Println(answer.AnswerStr) fmt.Println("服務端流式RPC===========================") stream, err := client.GetAnswer2(context.Background(), &question) if err != nil { log.Fatalf("fail to GetAnswer2: %v", err) } for { answer, err := stream.Recv() if err == io.EOF { break } if err != nil { log.Fatalf("%v.GetAnswer2, %v", client, err) } fmt.Println(answer.AnswerStr) } fmt.Println("客戶端流式RPC===========================") stream3, err := client.GetAnswer3(context.Background()) if err != nil { log.Fatalf("fail to GetAnswer3: %v", err) } for i := 1; i <= 3; i++ { question := test.Question{QuestionStr: fmt.Sprintf("問題%d", i)} if err = stream3.Send(&question); err != nil { log.Fatalf("fail to GetAnswer3 Send: %v", err) } } answer, err = stream3.CloseAndRecv() if err != nil { log.Fatalf("fail to GetAnswer3 CloseAndRecv: %v", err) } fmt.Println(answer.AnswerStr) fmt.Println("雙向流式RPC===============================") done := make(chan bool) stream4, err := client.GetAnswer4(context.Background()) if err != nil { log.Fatalf("fail to GetAnswer4: %v", err) } //接受服務端響應 go func() { for { answer, err := stream4.Recv() if err == io.EOF { close(done) return } if err != nil { log.Fatalf("%v.GetAnswer4, %v", client, err) } fmt.Println(answer.AnswerStr) } }() //客戶端發送請求 for i := 1; i <= 4; i++ { question := test.Question{QuestionStr: fmt.Sprintf("問題%d", i)} if err = stream4.Send(&question); err != nil { log.Fatalf("fail to GetAnswer3 Send: %v", err) } } stream4.CloseSend() <-done }
五、輸出
// 簡單RPC=========================== // Question:問題11111111?;Answer:Answer1。 // 服務端流式RPC=========================== // Question:問題11111111?;Answer:Answer1。 // Question:問題11111111?;Answer:Answer2。 // Question:問題11111111?;Answer:Answer3。 // 客戶端流式RPC=========================== // Question:問題1;Answer:Answer1。 // Question:問題2;Answer:Answer2。 // Question:問題3;Answer:Answer3。 // 雙向流式RPC=============================== // Question:問題1;Answer:Answer1。 // Question:問題2;Answer:Answer2。 // Question:問題3;Answer:Answer3。 // Question:問題4;Answer:Answer4。