gRPC & Go
You have probably heard of gRPC even if you haven’t used it. It is particularly popular for inter-service communication due to its efficiency, language-agnostic nature, and support out of the box for things like MTLS. Here’s a really simple example of how you can get started with gRPC and hopefully highlights some of the benefits.
For the examples here, I am using Buf which is currently my favourite protobuf tool. Firstly, define a service definition:
syntax = "proto3";
package example;
service ExampleService {
rpc ExampleMethod (ExampleRequest) returns (ExampleResponse);
}
message ExampleRequest {
string name = 1;
}
message ExampleResponse {
string message = 1;
}
Create a buf.gen.yaml
(you can also use Buf to generate this automatically). The one below basically says generate me both Go proto code and gRPC bindings:
version: v1
plugins:
- name: go
out: gen/go
- name: go-grpc
out: gen/go
Now you can run buf generate
. It will generate you a bunch of code in your gen/go
folder. Next, we will declare a server and implement the interface of the gRPC server, and start it:
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
pb "path/to/gen/go/example"
)
type server struct {
pb.UnimplementedExampleServiceServer
}
func (s *server) ExampleMethod(ctx context.Context, req *pb.ExampleRequest) (*pb.ExampleResponse, error) {
return &pb.ExampleResponse{Message: "Hello " + req.Name}, nil
}
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterExampleServiceServer(s, &server{})
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
A client can now connect as follows:
package main
import (
"context"
"log"
"time"
"google.golang.org/grpc"
pb "path/to/gen/go/example"
)
func main() {
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewExampleServiceClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.ExampleMethod(ctx, &pb.ExampleRequest{Name: "world"})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.Message)
}
That’s it!
Some next steps for you to consider
- Try and update the protobuf to add another service method, generate it, and implement it. This will be FAST!
- Add breaking change detection to your protobuf.
- Try and generate a client in another language (Java/Node/Python works pretty well).
- Try and build a protobuf “monorepo” that generates code in CI for multiple languages and does breaking change detection.
- Try and connect to your service without using
grpc.WithInsecure()
.
After these next steps, you will hopefully see some of the benefits of gRPC and will be ready to present it to your colleagues. Good luck!