grpc-go
Quick start
https://grpc.io/docs/languages/go/quickstart/
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
Regenerate gRPC code
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
helloworld/helloworld.proto
Basics tutorial
https://grpc.io/docs/languages/go/basics/
Server-side streaming RPC
func (s *routeGuideServer) ListFeatures(rect *pb.Rectangle, stream pb.RouteGuide_ListFeaturesServer) error {
for _, feature := range s.savedFeatures {
if inRange(feature.Location, rect) {
if err := stream.Send(feature); err != nil {
return err
}
}
}
return nil
}
Finally, as in our simple RPC, we return a nil error to tell gRPC that we’ve finished writing responses. Should any error happen in this call, we return a non-nil error; the gRPC layer will translate it into an appropriate RPC status to be sent on the wire
Client-side streaming RPC
func (s *routeGuideServer) RecordRoute(stream pb.RouteGuide_RecordRouteServer) error {
point, err := stream.Recv()
return stream.SendAndClose(&pb.RouteSummary{
PointCount: pointCount,
FeatureCount: featureCount,
Distance: distance,
ElapsedTime: int32(endTime.Sub(startTime).Seconds()),
})
Bidirectional streaming RPC
func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) error {
in, err := stream.Recv()
if err := stream.Send(note); err != nil {
Starting the server
flag.Parse()
lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", *port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
var opts []grpc.ServerOption
...
grpcServer := grpc.NewServer(opts...)
pb.RegisterRouteGuideServer(grpcServer, newServer())
grpcServer.Serve(lis)
Creating the client
var opts []grpc.DialOption
...
conn, err := grpc.Dial(*serverAddr, opts...)
if err != nil {
...
}
defer conn.Close()
client := pb.NewRouteGuideClient(conn)
Client-side streaming RPC
stream, err := client.RecordRoute(context.Background())
for _, point := range points {
if err := stream.Send(point); err != nil {
reply, err := stream.CloseAndRecv()
Bidirectional streaming RPC
stream, err := client.RouteChat(context.Background())
waitc := make(chan struct{})
go func() {
for {
in, err := stream.Recv()
if err == io.EOF {
// read done.
close(waitc)
return
}
for _, note := range notes {
if err := stream.Send(note); err != nil {
stream.CloseSend()
<-waitc
Generated-code reference
https://grpc.io/docs/languages/go/generated-code/
for individual streams, incoming and outgoing data is bi-directional but serial
Methods on generated server interfaces
On the server side, each service Bar
in the .proto
file results in the function
func RegisterBarServer(s *grpc.Server, srv BarServer)
Server-streaming methods
Foo(*MsgA, <ServiceName>_FooServer) error
<ServiceName>_FooServer
has an embedded grpc.ServerStream
and the following interface
type <ServiceName>_FooServer interface {
Send(*MsgB) error
grpc.ServerStream
}
End-of-stream for the server-to-client stream is caused by the return of the handler method
Client-streaming methods
type <ServiceName>_FooServer interface {
SendAndClose(*MsgA) error
Recv() (*MsgB, error)
grpc.ServerStream
}
Recv
returns (nil, io.EOF)
once it has reached the end of the stream
The single response message from the server is sent by calling the SendAndClose
method on this <ServiceName>_FooServer
parameter. Note that SendAndClose
must be called once and only once
Bidi-streaming methods
type <ServiceName>_FooServer interface {
Send(*MsgA) error
Recv() (*MsgB, error)
grpc.ServerStream
}