1、获取gRPC
环境变量GOPATH的src目录下执行:
git clone https://github.com/grpc/grpc-go.git google.golang.org/grpc
git clone https://github.com/golang/net.git golang.org/x/net
git clone https://github.com/golang/text.git golang.org/x/text
go get -u github.com/golang/protobuf/protoc-gen-go
git clone https://github.com/google/go-genproto.git google.golang.org/genproto
go install google.golang.org/grpc
2、proto文件
gRPC 允许定义4种类型的 service 方法。
(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
3、服务端
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。 ", 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) }
4、客户端
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 }
5、输出
// 简单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。