- Easy to reason about
- Text based protocol
- “Simple” to implement
- Slow to encode
- No support for streaming
- No concurrency
- Every client is slightly different
- Optimistic documentation
- Easy to inspect
- And yet another client to write
But Swagger OpenAPI
- Optimistic accuracy
- Disconnected from implementation and release
- Not mandatory
- Still JSON
- Harder to implement compatible changes
Wtf is protocol buffers (protobuf)
- Binary message format
- Default wire format for gRPC
- One of several supported formats
- Faster than JSON but you can find faster formats
- Generated interface
- Wide language support
- No JSON
syntax = "proto3";
package helloworld;
message HelloRequest {
string name = 1;
string colour = 2;
}
message HelloReply {
string message = 1;
bool authed = 2;
Most importantly
The G stands for
- 1.0 ‘g’ stands for ‘gRPC’
- 1.1 ‘g’ stands for ‘good’
- 1.2 ‘g’ stands for ‘green’
- 1.3 ‘g’ stands for ‘gentle’
- 1.4 ‘g’ stands for ‘gregarious’
- …
- 1.18 ‘g’ stands for ‘goose’
- 1.19 ‘g’ stands for ‘gold’
- 1.20 ‘g’ stands for ‘godric’
gRPC
- Many language implementations
- HTTP2
- Generated stubs
- Strongly typed and validated
- Bidirectional streaming
- No JSON
- Reflection implementation for auto discovery
- Generated stubs
- Standard middleware patterns
- Not having to write yet another client
The Proto Spec
syntax = "proto3";
option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
package helloworld;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
rpc SayLotsOfHellos (stream HelloRequest) \
returns (stream HelloReply) {}
message HelloRequest {
string name = 1;
string colour = 2;
}
message HelloReply {
string message = 1;
bool authed = 2;
}
Building the stubs
protoc -I helloworld/ helloworld/helloworld.proto \
--go_out=plugins=grpc:helloworld
The interface
type GreeterServer interface {
SayHello(context.Context, *HelloRequest) (*HelloReply, error)
}
func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) {
s.RegisterService(&_Greeter_serviceDesc, srv)
}
Building a server
type server struct{}
func (s *server) SayHello(
ctx context.Context,
in *pb.HelloRequest,
) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.Name)
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
func main() {
lis, err := net.Listen("tcp", 8080)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
And now a client
conn, _ := grpc.Dial(address, grpc.WithInsecure()
c := pb.NewGreeterClient(conn
r, err := c.SayHello(ctx, &pb.HelloRequest{
Name: "Bob",
}
fmt.Println(r.Message)
BORING
Time to tempt fate
TODO: Add Asciinema recordings
gRPC downsides
- Load Balancing -> http2 is still not supported by some LBs
- No direct support for browser clients
- Breaking API changes
- Poor documentation for some languages
- No standardisation across languages