# gRPC-go ref: - [AkhilSharma90 - FULL PROJECT - GO + GRPC](https://www.youtube.com/watch?v=a6G5-LUlFO4&ab_channel=AkhilSharma) - [gRPC - Quick Start](https://grpc.io/docs/languages/go/quickstart/#update-and-run-the-application) - [gRPC - Basics tutorial](https://grpc.io/docs/languages/go/basics/) ## Simply introduce types of gRPC (4 type) ### Unary - Similar to traditional API, like RESTful - **Client send request** and **Server send response** ### Server streaming - Client send request - Server send stream(data flow) ### Client streaming - Client send stream(data flow) - Server send request ### Bi-directional streaming - Both client and server send stream ## Building Environment * My notebook is windows 10 * libprotoc 3.19.4 * [Protocol Buffer Compiler Installation - Install pre-compiled binaries (any OS)](https://grpc.io/docs/protoc-installation/#install-pre-compiled-binaries-any-os) * [Notes on installing GRPC development environment for Go on Windows](https://gist.github.com/jjeffery/6e3a4d18ffbe1fc715be403f6391c5f4) * You can test by run `protoc --version` * Success ![](https://i.imgur.com/8ewsjSM.png) * Install the protocol compiler plugins for Go using the following commands: go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2 * run `go mod tidy` ## Creating proto files ```protobuf syntax="proto3"; // Files will get created in thin path option go_package = "./proto"; package greet_service; service GreetService{ // Unary rpc SayHello(NoParam) returns (HelloResponse); // Server stream rpc SayHelloServerStreaming(NamesList) returns (stream HelloResponse); // Client stream rpc SayHelloClientStreaming(stream HelloRequest) returns (MessagesLists); // Bi-directional stream rpc SayHelloBidirectionalStreaming(stream HelloRequest) returns (stream HelloResponse); } message NoParam{}; message HelloRequest{ string name = 1; } message HelloResponse{ string message = 1 ; } message NamesList{ repeated string names = 1; } message MessagesLists{ repeated string messages = 1; } ``` - proto files will be created when you run `protoc --go_out=. --go-grpc_out=. proto/greet.proto` ## main.go for Server and Client ### Server ```go // server/main.go package main import ( "log" "net" pb "grpc-go/proto" "google.golang.org/grpc" ) const ( port = ":8080" ) type helloServer struct { pb.GreetServiceServer } func main() { lis, err := net.Listen("tcp", port) if err != nil { log.Fatalf("Failed to start the server %v", err) } grpcServer := grpc.NewServer() pb.RegisterGreetServiceServer(grpcServer, &helloServer{}) log.Printf("Server started at %v", lis.Addr()) if err := grpcServer.Serve(lis); err != nil { log.Fatalf("Failed to start: %v", err) } } ``` ### Client ```go // client/main.go package main import ( pb "grpc-go/proto" "log" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) const ( port = ":8080" ) func main() { conn, err := grpc.Dial("localhost"+port, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { log.Fatalf("Did not connect: %v", err) } defer conn.Close() client := pb.NewGreetServiceClient(conn) // If testing Unary , comment out this names := &pb.NamesList{ Names: []string{"Alice", "Bob", "Joe"}, } // Discomment out to test // Unary // callSayHello(client) // Server stream // callSayHelloServerStream(client, names) // Client stream // callSayHelloClientStream(client, names) // Bi-directional stream // callSayHelloBidirectionalStream(client, names) } ``` ## Unary ![](https://i.imgur.com/FZfjTDl.png) ### Server ```go // server/unary.go package main import ( "context" pb "grpc-go/proto" ) func (s *helloServer) SayHello(ctx context.Context, req *pb.NoParam) (*pb.HelloResponse, error) { // Send response return &pb.HelloResponse{ Message: "Hello", }, nil } ``` ### Client ```go // client/unary.go package main import ( "context" pb "grpc-go/proto" "log" "time" ) func callSayHello(client pb.GreetServiceClient) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() res, err := client.SayHello(ctx, &pb.NoParam{}) if err != nil { log.Fatalf("Could not greet: %v", err) } log.Printf("%s", res.Message) } ``` ## Server Stream ![](https://i.imgur.com/cLx4z9d.png) ### Server ```go // server/server_stream.go package main import ( "log" "time" pb "grpc-go/proto" ) func (s *helloServer) SayHelloServerStreaming(req *pb.NamesList, stream pb.GreetService_SayHelloServerStreamingServer) error { log.Printf("Got request with names: %v", req.Names) // Send response stream for _, name := range req.Names { res := &pb.HelloResponse{ Message: "Hello " + name, } if err := stream.Send(res); err != nil { return err } time.Sleep(2 * time.Second) } return nil } ``` ### Client ```go // client/server_stream.go package main import ( "context" "io" "log" pb "grpc-go/proto" ) func callSayHelloServerStream(client pb.GreetServiceClient, names *pb.NamesList) { log.Printf("Streaming Started") // Send request stream, err := client.SayHelloServerStreaming(context.Background(), names) if err != nil { log.Fatalf("Could not send names: %v", err) } for { // Receive response stream message, err := stream.Recv() if err == io.EOF { break } if err != nil { log.Fatalf("Error while streaming %v", err) } log.Println(message) } log.Printf("Streaming finished.") } ``` ## Client Stream ![](https://i.imgur.com/yWcnS8O.png) ### Server ```go // server/client_stream.go package main import ( pb "grpc-go/proto" "io" "log" ) func (s *helloServer) SayHelloClientStreaming(stream pb.GreetService_SayHelloClientStreamingServer) error { var messages []string for { // Receive request stream req, err := stream.Recv() if err == io.EOF { // Send response return stream.SendAndClose(&pb.MessagesLists{Messages: messages}) } if err != nil { return err } log.Printf("Got request with name: %v", req.Name) messages = append(messages, "Hello", req.Name) } } ``` ### Client ```go // client/client_stream.go package main import ( "context" pb "grpc-go/proto" "log" "time" ) func callSayHelloClientStream(client pb.GreetServiceClient, names *pb.NamesList) { log.Printf("Client streaming started.") stream, err := client.SayHelloClientStreaming(context.Background()) if err != nil { log.Fatalf("Could not send names: %v", err) } // Send request stream for _, name := range names.Names { req := &pb.HelloRequest{ Name: name, } if err := stream.Send(req); err != nil { log.Fatalf("Error while sending %v", err) } log.Printf("Sent the request with name: %s", name) time.Sleep(2 * time.Second) } // Receive response res, err := stream.CloseAndRecv() log.Printf("Client streaming finished") if err != nil { log.Fatalf("Error while receiving %v", err) } log.Printf("%v", res.Messages) } ``` ## Bi-directional stream ![](https://i.imgur.com/Lyj7cfO.png) ### Server ```go // server/bi_stream.go package main import ( pb "grpc-go/proto" "io" "log" ) func (s *helloServer) SayHelloBidirectionalStreaming(stream pb.GreetService_SayHelloBidirectionalStreamingServer) error { for { // Receive request stream req, err := stream.Recv() if err == io.EOF { return nil } if err != nil { return err } log.Printf("Got request with name: %v", req.Name) // Send response stream res := &pb.HelloResponse{ Message: "Hello " + req.Name, } if err := stream.Send(res); err != nil { return err } } } ``` ### Client ```go // client/bi_stream.go package main import ( "context" pb "grpc-go/proto" "io" "log" "time" ) func callSayHelloBidirectionalStream(client pb.GreetServiceClient, names *pb.NamesList) { log.Printf("Bidirectional streaming started.") stream, err := client.SayHelloBidirectionalStreaming(context.Background()) if err != nil { log.Fatalf("Could not send names: %v", err) } // Receive response stream go func() { for { message, err := stream.Recv() if err == io.EOF { break } if err != nil { log.Fatalf("Error while streaming %v", err) } log.Println(message) } }() // Send request stream for _, name := range names.Names { req := &pb.HelloRequest{ Name: name, } if err := stream.Send(req); err != nil { log.Fatalf("Error while sending %v", err) } time.Sleep(2 * time.Second) } stream.CloseSend() log.Printf("Bidirectional streaming finished.") } ```