Building Scalable Microservices with gRPC in Golang: A Step-by-Step Guide

Nasri Adzlani
3 min readAug 22, 2024

--

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

--

--

No responses yet