Post

gRPC with golang

intro

  • sample three timer gRPC service in a golang.
  • when input 1, returns 3
  • works in a stream

preview

1
2
3
4
5
6
7
8
9
10
11
./client
2020/02/10 12:11:11 Got message : (0 attempt: _three_timed 0)
2020/02/10 12:11:12 Got message : (1 attempt: _three_timed 3)
2020/02/10 12:11:12 Got message : (2 attempt: _three_timed 6)
2020/02/10 12:11:13 Got message : (3 attempt: _three_timed 9)
2020/02/10 12:11:13 Got message : (4 attempt: _three_timed 12)
2020/02/10 12:11:14 Got message : (5 attempt: _three_timed 15)
2020/02/10 12:11:14 Got message : (6 attempt: _three_timed 18)
2020/02/10 12:11:15 Got message : (7 attempt: _three_timed 21)
2020/02/10 12:11:15 Got message : (8 attempt: _three_timed 24)
2020/02/10 12:11:16 Got message : (9 attempt: _three_timed 27)

howto

  • install protocol buffer (using homebrew on OSX)
1
brew install protobuf
  • install protoc-gen-go to use --go_out and move it(make symbolic link) to the $PATH
1
2
3
go get -u github.com/golang/protobuf/protoc-gen-go
cd /usr/local/bin
ln -s $GOPATH/bin/protoc-gen-go
  • generate stub of golang using protoc
1
2
cd grpc-tester
protoc -I proto/v1/ --go_out=plugins=grpc:proto/v1 proto/v1/threetimer.proto

sample codes

  • protobuf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
syntax = "proto3";

package threetimer;

// gRPC service definition
service ThreeTimesService {
    // input as a stream, output also as a stream
    rpc ThreeTimes(stream Data) returns (stream Data) {}
}

// protobuf message definition
message Data {
    string name = 1;
    int32 value = 2;
}
  • server side
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
type threeTimerServer struct {
	pb.UnimplementedThreeTimesServiceServer
}

func main() {
	lis, err := net.Listen("tcp", "localhost:5000")
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}

	server := grpc.NewServer()
	srv := new(threeTimerServer)
	pb.RegisterThreeTimesServiceServer(server, srv)

	err = server.Serve(lis)
	if err != nil {
		fmt.Println(err)
	}
}

func (*threeTimerServer) ThreeTimes(stream pb.ThreeTimesService_ThreeTimesServer) error {
	for {
		in, err := stream.Recv()
		if err == io.EOF {
			return nil
		}
		if err != nil {
			return err
		}

		out := &pb.Data{
			Name:  in.Name + "_three_timed",
			Value: in.Value * 3,
		}

		if err := stream.Send(out); err != nil {
			return err
		}
	}
}
  • client side
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
conn, err := grpc.Dial("localhost:5000", grpc.WithInsecure(), grpc.WithBlock())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	c := pb.NewThreeTimesServiceClient(conn)

	// Contact the server and print out its response.
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	stream, err := c.ThreeTimes(ctx)
	if err != nil {
		log.Fatal(err)
	}

	waitChan := make(chan struct{})
	go func() {
		for {
			in, err := stream.Recv()
			if err == io.EOF {
				// read done.
				close(waitChan)
				return
			}
			if err != nil {
				log.Fatalf("Failed to receive : %v", err)
			}
			log.Printf("Got message : (%v %v)", in.Name, in.Value)
		}
	}()

	for i := 0; i < 10; i++ {
		time.Sleep(500 * time.Millisecond)
		if err := stream.Send(&pb.Data{
			Name:  fmt.Sprint(i) + " attempt: ",
			Value: int32(i),
		}); err != nil {
			log.Fatalf("Failed to send : %v", err)
		}
	}
	stream.CloseSend()
	<-waitChan

references

  • https://github.com/fransoaardi/grpc-tester
  • https://grpc.io/
This post is licensed under CC BY 4.0 by the author.

Comments powered by Disqus.