gRPC_Go

grpc

In gRPC, a client application can directly call a method on a server application on a different machine as if it were a local object, making it easier for you to create distributed applications and services. As in many RPC systems, gRPC is based around the idea of defining a service, specifying the methods that can be called remotely with their parameters and return types. On the server side, the server implements this interface and runs a gRPC server to handle client calls. On the client side, the client has a stub (referred to as just a client in some languages) that provides the same methods as the server.

grpc

It is a protocol that is build on top of HTTP/2, and it has some handy features. These included the ability to make streaming calls from the client and server side, or Bi-directional streaming. It serializes and deserializes data using Protocol Buffers, and it also provides code generation through the gRPC compiler to currently 11 different languages.

gRPC clients and servers can run and talk to each other in a variety of environments - from servers inside Google to your own desktop - and can be written in any of gRPC’s supported languages. So, for example, you can easily create a gRPC server in Java with clients in Go, Python, or Ruby. In addition, the latest Google APIs will have gRPC versions of their interfaces, letting you easily build Google functionality into your applications

No input rpc parameter defines
If you don’t want any input or output parameters, you can use the well-known proto google.protobuf.Empty. However, this is discouraged as it prevents you from adding parameters to the method in the future. Instead, you would be encouraged to follow the normal practice of having a message for the request, but simply with no contents.

1
2
3
4
5
service Greeter {
rpc SayHello (SayHelloRequest) returns (SayHelloResponse) {}
}

message SayHelloRequest {} // service has no input

implicit stream context
stream also has context as well like unary rpc call which passed context explicitly, while for stream it’s implicitly!

1
2
ctx := stream.Context() 
<-ctx.Done

Metadata
Metadata is information about a particular RPC call (such as authentication details) in the form of a list of key-value pairs, where the keys are strings and the values are typically strings, but can be binary data. Metadata is opaque to gRPC itself - it lets the client provide information associated with the call to the server and vice versa.

Access to metadata is language dependent

http2

As grpc based on http2, in order to understand deeply, we need to learn some core concepts of http2.
A “stream” is an independent, bidirectional sequence of frames exchanged between the client and server within an HTTP/2 connection.

  • Stream

    A bidirectional flow of bytes within an established connection, which may carry one or more messages.

    • A single HTTP/2 connection can contain multiple concurrently open streams, with either endpoint interleaving frames from multiple streams.
    • Streams can be established and used unilaterally or shared by either the client or server.
    • Streams can be closed by either endpoint.
    • Streams are identified by an integer. Stream identifiers are assigned to streams by the endpoint initiating the stream.
  • Message

    A complete sequence of frames that map to a logical request or response message.

  • Frame

    The smallest unit of communication in HTTP/2, each containing a frame
    header, which at a minimum identifies the stream to which the frame belongs.

    • All communication is performed over a single TCP connection that can carry any number of bidirectional streams.
    • Each stream has a unique identifier and optional priority information that is used to carry bidirectional messages.
    • Each message is a logical HTTP message, such as a request, or response, which consists of one or more frames.
    • The frame is the smallest unit of communication that carries a specific type of data—e.g., HTTP headers, message payload, and so on. Frames from different streams may be interleaved and then reassembled via the embedded stream identifier in the header of each frame.
drawing

Stream is not http2 connection(mostly tcp connection), it’s also bidirectional stuff, close stream does not means close its underlaying connection.

Unary RPC call

gRPC service methods have exactly one input message and exactly one output message, handler is called only when its get the whole message. Typically, these messages are used as input and output to only one method. This is on purpose, as it allows easily adding new parameters later (to the messages) while maintaining backward compatibility.

In a unary rpc call, the client sends a single request and the server responds with a single message.

pros and cons

  • easy to use
  • only effective to small data
  • if data is huge, it causes delay to response
  • no interactive support.

writing service in proto

greet.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
syntax = "proto3";
package greet;
//used by proto itself(independent with different language)
// to prevent naming conflicts between different projects(protos).
// import "google/protobuf/timestamp.proto";

option go_package = "github.com/hello/runtime/proto/greet";
// used by protoc when generate go specific code

// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
string name = 1;
}

// The response message containing the greetings
message HelloReply {
string message = 1;
}

build

Prerequisite

1
2
3
4
5
6
7
8
$ wget -O ./protoc-3.15.8-linux-x86_64.zip https://github.com/protocolbuffers/protobuf/releases/download/v3.15.8/protoc-3.15.8-linux-x86_64.zip
$ unzip protoc-3.15.8-linux-x86_64.zip -d /usr/local

# Install the protocol compiler plugins for Go using the following commands
# protoc-gen-go: go plugin or gogo/protobuf
# proto-gen-go-grpc: go rpc plugin
$ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.26
$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1

compile rpc
if multiple protos belong to same package, you must provide all of them to protoc command!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# put your source at $GOPATH/src/github.com/hello
$ cd $GOPATH/src/github.com/hello

# If the paths=import flag is specified, the output file is placed in a directory named after the Go package's import path For example, an input file pro/hello.proto with a Go import path of github.com/hello/runtime/proto/greet results in an output file at github.com/hello/runtime/proto/greet/hello.pb.go. This is the default output mode if a paths flag is not specified.

# If the paths=source_relative flag is specified, the output file is placed in the same relative directory as the input file. For example, an input file pro/hello.proto results in an output file at pro/hello.pb.go

# --go_out and --go-grpc_out is based path, all other paths are relative to it!!!

$ protoc --go_out=$GOPATH/src/ --go_opt=paths=import --go-grpc_out=$GOPATH/src/ --go-grpc_opt=paths=import proto/greet.proto -I=xxx/other/proto:protocols/
# generated code at $GOPATH/src/github.com/hello/runtime/proto/greet

$ protoc --go_out=$GOPATH/src/ --go_opt=paths=source_relative --go-grpc_out=$GOPATH/src/ --go-grpc_opt=paths=source_relative proto/greet.proto -I=xxx/other/proto:protocols/
# generated code at $GOPATH/src/proto

$ protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative proto/hello.proto
# generated code at ./proto

Implement rpc

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
package main

import (
"context"
"log"
"net"

"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
pb "github.com/hello/runtime/protocols/greet"
"google.golang.org/grpc/reflection"
)

const (
port = ":50051"
)

// server is used to implement greet.GreeterServer.
type server struct {
pb.UnimplementedGreeterServer // must be first field
// your staff here
}

/// return value: HelloReply, error
// SayHello implements greet.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
md, ok := metadata.FromIncomingContext(ctx)
if ok {
// metadata is map: var MD map[string][]string
// key is string, while value is string array!!!
log.Printf("metadata: %v", md.Get("k1"))
}
log.Printf("Received: %v", in.GetName())
// connection is close when returns!!!
return &pb.HelloReply{Message: "Hi " + in.GetName()}, nil
}

func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
// register services(server{}) with rpc server
pb.RegisterGreeterServer(s, &server{})

// for debugging
reflection.Register(s)
log.Printf("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %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
package main

import (
"context"
"fmt"
"log"

pb "github.com/hello/proto"
"google.golang.org/grpc"
)

func main() {
fmt.Println("Client..")

con, err := grpc.Dial("localhost:50051", opts, grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
log.Fatalf("Error connecting: %v \n", err)
}

defer con.Close()
//client for specific service
c := pb.NewGreeterClient(con)
req := pb.HelloRequest{Name: "tom"}
res, err := c.SayHello(context.Background(), &req)
if err != nil {
log.Fatalf("Error on Echo rpc call: %v\n", err)
} else {
fmt.Printf("Response: %v\n", res)
}
}

client stream rpc call

In a client streaming rpc call, the client sends a bunch of requests and once it is done streaming, the server will return a single message.

stream here is stream of particular request message, the unit is request but byte!!

Making a client stream request is useful when the client needs to send resources one by one to the server, so they can be processed right away, and once the streaming call is finished the client gets the response.

For client stream, client side needs a stream to send and also server needs a stream to receive messages, both side need to change.

Steps

  • client send one request, then another

  • client notify server I finished the stream

  • server receive request one by one

  • server see client finish the stream send one reply.

server in proto

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
syntax = "proto3";
package greet;
//used by proto itself(independent with different language)
// to prevent naming conflicts between different projects(protos).
// import "google/protobuf/timestamp.proto";

option go_package = "github.com/hello/runtime/protocols/greet";
// used by protoc when generate go specific code

service Greeter {
// Sends a greeting
rpc SayHello (stream HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
string name = 1;
}

// The response message containing the greetings
message HelloReply {
string message = 1;
}

build

Same as unary rpc

implement rpc

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
package main

import (
"fmt"
"io"
"log"
"net"
"strings"

pb "github.com/hello/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)

const (
port = ":50051"
)

// server is used to implement greet.GreeterServer.
type greetServer struct {
pb.UnimplementedGreeterServer // must be first field
// your staff here
}

// SayHello implements greet.GreeterServer
func (s *greetServer) SayHello(stream pb.Greeter_SayHelloServer) error {
data := []string{}
for {
req, err := stream.Recv()
if err == io.EOF {
// response message is returned through stream, not return value for unary rpc call
return stream.SendAndClose(&pb.HelloReply{Message: strings.Join(data, ",")})
}

if err != nil {
return fmt.Errorf("internal error")
}

data = append(data, req.GetName())
}
}

func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
// register services(server{}) with rpc server
pb.RegisterGreeterServer(s, &greetServer{})
// for debugging
reflection.Register(s)
log.Printf("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %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
package main

import (
"context"
"fmt"
"log"
"time"

pb "github.com/hello/proto"
"google.golang.org/grpc"
)

func main() {
fmt.Println("Client..")

con, err := grpc.Dial("localhost:50051", opts, grpc.WithInsecure(), grpc.WithBlock())

if err != nil {
log.Fatalf("Error connecting: %v \n", err)
}

defer con.Close()
//client for specific service
c := pb.NewGreeterClient(con)
req1 := pb.HelloRequest{Name: "tom"}
req2 := pb.HelloRequest{Name: "jack"}

// rpc retuns a stream handler
stream, err := c.SayHello(context.Background())
if err != nil {
log.Fatalf("Error on Echo rpc call: %v\n", err)
} else {
// send several requests through stream
err = stream.Send(&req1)
if err != nil {
log.Fatalf("Error on sending: %v\n", err)
}
fmt.Printf("sent: %v\n", req1.GetName())
time.Sleep(10 * time.Second)
err = stream.Send(&req2)
if err != nil {
log.Fatalf("Error on sending: %v\n", err)
}
fmt.Printf("sent: %v\n", req2.GetName())
}

// this will call CloseSend to notify I sent all, then receive
res, err := stream.CloseAndRecv()
if err != nil {
log.Fatalf("Error on recv: %v\n", err)
} else {
fmt.Printf("Response :%s\n", res.GetMessage())
}
}

server stream rpc call

For server stream, server side needs a stream to send and also client needs a stream to receive response messages, both side need to change.

server in proto

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
syntax = "proto3";
package greet;
//used by proto itself(independent with different language)
// to prevent naming conflicts between different projects(protos).
// import "google/protobuf/timestamp.proto";

option go_package = "github.com/hello/runtime/protocols/greet";
// used by protoc when generate go specific code

service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (stream HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
string name = 1;
}

// The response message containing the greetings
message HelloReply {
string message = 1;
}

build

Same as unary rpc

implement rpc

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
package main

import (
"fmt"
"log"
"net"
"time"

pb "github.com/hello/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)

const (
port = ":50051"
)

// server is used to implement greet.GreeterServer.
type greetServer struct {
pb.UnimplementedGreeterServer // must be first field
// your staff here
}

// SayHello implements greet.GreeterServer
func (s *greetServer) SayHello(req *pb.HelloRequest, stream pb.Greeter_SayHelloServer) error {
res1 := pb.HelloReply{Message: "hello: " + req.GetName()}
err := stream.Send(&res1)
if err != nil {
fmt.Println("error while sending response")
}

time.Sleep(5 * time.Second)

res2 := pb.HelloReply{Message: "hi: " + req.GetName()}
err = stream.Send(&res2)
// context = stream.Context() get context if needs
if err != nil {
fmt.Println("error while sending response")
}

// stream is close when it returns
return nil
}

func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
// register services(server{}) with rpc server
pb.RegisterGreeterServer(s, &greetServer{})
reflection.Register(s) // for testing
log.Printf("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %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
package main

import (
"context"
"fmt"
"io"
"log"

pb "github.com/hello/proto"
"google.golang.org/grpc"
)

func main() {
fmt.Println("Client..")

opts := grpc.WithInsecure()
// WithBlock() blocks here until, error or connection is setup
con, err := grpc.Dial("localhost:50051", opts, grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
log.Fatalf("Error connecting: %v \n", err)
}

defer con.Close()
//client for specific service
c := pb.NewGreeterClient(con)
req1 := pb.HelloRequest{Name: "tom"}

stream, err := c.SayHello(context.Background(), &req1)
if err != nil {
log.Fatalf("Error on sending: %v\n", err)
} else {
for {
res, err := stream.Recv()
if err == io.EOF {// server close the stream, all is done!!!
break
}
if err != nil {
fmt.Println(err)
break
}
fmt.Printf("response: %v\n", res)
}
}
}

bi-direction stream rpc call

In a bi-directional streaming rpc call, both the client and the server sends multiple messages to each other. Using this type of rpc call, can be a little bit more complicated, since you have to take care of error handling from the server side and the client side, plus in some cases it can add more latency. And perhaps it could be a better option to use a unary call.

Both the client and server can stop receiving or sending messages at any point in time, either because some errors occurred or because some other business logic happened.

server in proto

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
syntax = "proto3";
package greet;
//used by proto itself(independent with different language)
// to prevent naming conflicts between different projects(protos).
// import "google/protobuf/timestamp.proto";

option go_package = "github.com/hello/runtime/protocols/greet";
// used by protoc when generate go specific code

service Greeter {
// Sends a greeting
rpc SayHello (stream HelloRequest) returns (stream HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
string name = 1;
}

// The response message containing the greetings
message HelloReply {
string message = 1;
}

build

Same as unary rpc

implement rpc

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
package main

import (
"fmt"
"io"
"log"
"net"

pb "github.com/hello/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)

const (
port = ":50051"
)

// server is used to implement greet.GreeterServer.
type greetServer struct {
pb.UnimplementedGreeterServer // must be first field
// your staff here
}

// SayHello implements greet.GreeterServer
func (s *greetServer) SayHello(stream pb.Greeter_SayHelloServer) error {
for {
req, err := stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return fmt.Errorf("server recv error")
} else {
err = stream.Send(&pb.HelloReply{Message: "hello " + req.GetName()})
if err == io.EOF {
return nil
}
if err != nil {
return fmt.Errorf("server send error")
}
}
}
}

func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
// register services(server{}) with rpc server
pb.RegisterGreeterServer(s, &greetServer{})

// for debugging
reflection.Register(s)
log.Printf("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %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
68
package main

import (
"context"
"fmt"
"io"
"log"
"sync"
"time"

pb "github.com/hello/proto"
"google.golang.org/grpc"
)

func main() {
fmt.Println("Client..")

// WithBlock() blocks here until, error or connection is setup
con, err := grpc.Dial("localhost:50051", opts, grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
log.Fatalf("Error connecting: %v \n", err)
}

defer con.Close()
//client for specific service
c := pb.NewGreeterClient(con)
// rpc retuns a stream handler
stream, err := c.SayHello(context.Background())
if err != nil {
log.Fatalf("Error on Echo rpc call: %v\n", err)
}
// start a goroutine to send
go func() {
req1 := pb.HelloRequest{Name: "tom"}
stream.Send(&req1)
fmt.Println("sent: tom")

time.Sleep(time.Second * 5)
req2 := pb.HelloRequest{Name: "jack"}
stream.Send(&req2)
fmt.Println("sent: jack")

// tell sever sending is done, connection is still alive, but stream is close
stream.CloseSend()
fmt.Println("sending done")
}()

var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
for {
res, err := stream.Recv()
if err == io.EOF {
return
}

if err != nil {
// continue next
fmt.Println("error in recv")
continue
}
fmt.Printf("Response: %v\n", res)
}
}()
// block until receive io.EOF
wg.Wait()
}

Debug grpc

There are two tools can be used as grpc client to test grpc server, see below

grpcui(web)

On way to edit source code to include reflection in your grpc server or without change see below

1
2
3
4
5
+ import "google.golang.org/grpc/reflection"
s := grpc.NewServer()
pb.RegisterGreeterService(s, &pb.GreeterService{SayHello: sayHello})
+ // Register reflection service on gRPC server.
+ reflection.Register(s)

Run grpcui as below

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ GO111MODULE=on go install github.com/fullstorydev/grpcui/cmd/grpcui@latest
# -bind: webserver address
# -port: webserver port
# -plaintext: connect grpc server without tls
# localhost:50051: grpc server
# With this command, you must register your rpc with reflection as above
$ grpcui -open-browser=false -bind=10.0.2.15 -port=8000 -plaintext localhost:50051

# if you can NOT change your source code, there is an another way to run grpcui
# xxx.proto who defines rpc call and message.
# -proto can be relative path
$ grpcui -open-browser=false -proto=./path/to/xxx.proto -bind=10.0.2.15 -port=8000 -plaintext localhost:50051

# More advanced, if your xxx.proto import other protos, you need to add -import-path to let grpcui to find them
# other protos path relative to --import-path(used for imported protos)
$ grpcui -open-browser=false -proto=./path/to/xxx.proto -import-path=/path/to/depen/ -bind=10.0.2.15 -port=8000 -plaintext localhost:50051

# then open browser at http://10.0.2.15:8000/

command line tool

On way to edit source code to include reflection in your grpc server or without change see below

1
2
3
4
5
+ import "google.golang.org/grpc/reflection"
s := grpc.NewServer()
pb.RegisterGreeterService(s, &pb.GreeterService{SayHello: sayHello})
+ // Register reflection service on gRPC server.
+ reflection.Register(s)

Run grpcurl command

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
$ GO111MODULE=on go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest

# list all services
$ grpcurl -plaintext localhost:50051 list
grpc.reflection.v1alpha.ServerReflection
greet.Greeter

# list all methods
$ grpcurl -plaintext localhost:50051 list greet.Greeter
greet.Greeter.SayHello

# desribe all methods
$ grpcurl -plaintext localhost:50051 describe greet.Greeter
greet.Greeter is a service:
service Greeter {
rpc SayHello ( .greet.HelloRequest ) returns ( .greet.HelloReply );
}

# describe message type
$ grpcurl -plaintext localhost:50051 describe greet.HelloRequest
greet.HelloRequest is a message:
message HelloRequest {
string name = 1;
}

# call rpc
$ grpcurl -plaintext -d '{"name": "jason"}' localhost:50051 greet.Greeter.SayHello
{
"message": "Hi jason"
}

# if you can't register your grpc server with reflection, you can pass -proto and -import-path to grpcurl as grpcui does!!

gogo

gogoprotobuf is a fork of golang/protobuf with extra code generation features.

This code generation is used to achieve:

  • fast marshalling and unmarshalling
  • more canonical Go structures
  • goprotobuf compatibility
  • less typing by optionally generating extra helper code
  • peace of mind by optionally generating test and benchmark code
  • other serialization formats

gogo depends on grpc when generates grpc stub code, that means it justs wrapper grpc only. so for grpc, you must install grpc as well. go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.3.2, make sure install proper version tested by gogo.

Install

Choose one binary and install it, different binaries have different speed and customization. more refer to gogo.

Usage

1
2
3
4
5
6
7
8
# can only github.com/golang/protobuf/proto
protoc --gofast_out=. myproto.proto

# explicit use gogo github.com/golang/protobuf/proto
protoc -I=. -I=$GOPATH/src -I=$GOPATH/src/github.com/golang/protobuf/proto --{binary}_out=. myproto.proto

# use gogo github.com/golang/protobuf/proto by default
protoc --gofast_out=plugins=grpc:. my.proto

pros and cons