Building Scalable Microservices with gRPC in Golang: A Step-by-Step Guide
gRPC is a modern open source high performance Remote Procedure Call (RPC) framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication. It is also applicable in last mile of distributed computing to connect devices, mobile applications and browsers to backend services.
Why Golang and gRPC?
Golang’s simplicity, concurrency, and static typing, combined with gRPC’s efficient binary serialization, make it a powerful combination for building scalable and maintainable microservices. The strong typing provided by Protocol Buffers ensures consistency and reduces errors, while Golang’s tooling and the active community support make development a pleasure.
What is Protocol Buffers?
Protocol buffers are Google’s language-neutral, platform-neutral, extensible mechanism for serializing structured data — think XML, but smaller, faster, and simpler. You define how you want your data to be structured once, then you can use special generated source code to easily write and read your structured data to and from a variety of data streams and using a variety of languages.
Example:
syntax = "proto3";
option go_package = "grpc-test/proto/user";
service DataUser {
rpc GetUser (GetUserRequest) returns (GetUserResponse) {}
rpc ListUser (ListUserRequest) returns (ListUserResponse) {}
}
message GetUserRequest {
string id = 1;
string name = 2;
}
message GetUserResponse {
string id = 1;
string name = 2;
string email = 3;
}
message ListUserRequest {
string id = 1;
string name = 2;
}
message ListUserResponse {
repeated GetUserResponse users = 1;
}
code at proto is like struct and interface repository.
Compile a proto file into golang file
i have protofile in proto folder at project directory with ‘user.proto’ filename
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
proto/user.proto
after compiling successfully, compiler will be generate 2 golang file user_grpc.pb.go and user.pb.go
How to make a gRPC server
create a file main.go at project directory for server
package main
import (
"flag"
"fmt"
pb "grpc-test/proto"
"grpc-test/service"
"log"
"net"
"google.golang.org/grpc"
)
var (
port = flag.Int("port", 50051, "The server port")
)
func main() {
flag.Parse()
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterDataUserServer(s, &service.Server{})
log.Printf("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
and i have specific folder and file for services ‘service/user.service.go’
package service
import (
"context"
"fmt"
pb "grpc-test/proto"
)
type User struct {
Id int32
Name string
Email string
}
type Server struct {
pb.UnimplementedDataUserServer
}
func (s *Server) GetUser(ctx context.Context, in *pb.GetUserRequest) (*pb.GetUserResponse, error) {
fmt.Println("hello ", in.Id)
return &pb.GetUserResponse{Id: in.Id, Name: "test", Email: "testing@mail.com"}, nil
}
func (s *Server) ListUser(ctx context.Context, in *pb.ListUserRequest) (*pb.ListUserResponse, error) {
return &pb.ListUserResponse{Users: []*pb.GetUserResponse{
{Id: "1", Name: "test", Email: "HEHE@mail.com"},
{Id: "2", Name: "test2", Email: "wwkwkwk@mail.com"},
}}, nil
}
and this is a my folder structure
├── go.mod
├── go.sum
├── main.go
├── proto
│ ├── user.pb.go
│ ├── user.proto
│ └── user_grpc.pb.go
├── service
│ ├── user.service.go
│ └── user_test.service.go
└── tmp
└── main
Now, Create a Client
Create a project directory for a client and create golang file like a main
Connect to a grpc server like this
flag.Parse()
// Set up a connection to the server.
addr := flag.String("addr", "localhost:50051", "the address to connect to")
conn, err := grpc.NewClient(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
Change a port and matching to your grpc server port