gRPC in Golang: Building Fast and Scalable Services
Introduction
Imagine you’re building an app where multiple services need to talk to each other—like a shopping app where one service handles products, another handles payments, and another manages user accounts. For services to talk fast, especially when scaling up, gRPC is a great solution. Let's break down what gRPC is, why it’s useful, and how to set it up in Golang.
Section 1: What is gRPC?
gRPC (gRPC Remote Procedure Call) is a high-performance framework that lets programs communicate over the network. Created by Google, it uses HTTP/2 for faster communication and Protocol Buffers (protobufs) for defining the data structures, which are more efficient than JSON.
Key points to highlight:
Language agnostic – Works with multiple languages (not just Golang).
Fast and efficient – Uses HTTP/2 for multiplexing requests and Protocol Buffers for optimized data serialization.
Supports streaming – Allows real-time data streams between client and server.
Section 2: Setting Up gRPC in Golang
Let’s go through setting up gRPC in Golang. We’ll build a basic service that can get and set user information.
Step 1: Install gRPC and Protocol Buffers
In your project directory, start by installing the following dependencies:
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
Make sure protoc
is also installed on your system. This will compile our .proto
files into Go code.
Step 2: Define the Protocol Buffer File
In gRPC, you define your service interface and data structures in a .proto
file. Create a file named user.proto
:
syntax = "proto3";
package user;
service UserService {
rpc GetUser (UserRequest) returns (UserResponse);
rpc SetUser (UserRequest) returns (UserResponse);
}
message UserRequest {
string name = 1;
}
message UserResponse {
string message = 1;
}
Here’s what’s happening:
service UserService defines our service, which has two RPC methods:
GetUser
andSetUser
.UserRequest and UserResponse define the message structures used by our methods.
UserRequest
takes aname
, andUserResponse
returns amessage
.
Step 3: Generate Go Code from the Proto File
Run this command to generate the Go code:
protoc --go_out=. --go-grpc_out=. user.proto
This generates files with Go code for our protobuf and gRPC services.
Section 3: Implementing the Server
Now that we have our gRPC code, let’s implement the server. Create a file named server.go
:
package main
import (
"context"
"fmt"
"log"
"net"
"google.golang.org/grpc"
pb "path/to/your/generated/proto/files"
)
type server struct {
pb.UnimplementedUserServiceServer
}
func (s *server) GetUser(ctx context.Context, req *pb.UserRequest) (*pb.UserResponse, error) {
message := fmt.Sprintf("Hello, %s!", req.Name)
return &pb.UserResponse{Message: message}, nil
}
func (s *server) SetUser(ctx context.Context, req *pb.UserRequest) (*pb.UserResponse, error) {
message := fmt.Sprintf("User %s has been saved!", req.Name)
return &pb.UserResponse{Message: message}, nil
}
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
grpcServer := grpc.NewServer()
pb.RegisterUserServiceServer(grpcServer, &server{})
log.Printf("Server listening at %v", lis.Addr())
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("Failed to serve: %v", err)
}
}
Explanation:
Define the Server – The
server
struct implements theUserService
interface.Implement Methods –
GetUser
andSetUser
methods generate and return aUserResponse
.Start the Server – In
main
, we set up a TCP listener on port50051
, create a new gRPC server, and register ourUserService
.
Section 4: Implementing the Client
To test this out, we’ll create a client in client.go
.
package main
import (
"context"
"log"
"time"
"google.golang.org/grpc"
pb "path/to/your/generated/proto/files"
)
func main() {
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
if err != nil {
log.Fatalf("Did not connect: %v", err)
}
defer conn.Close()
client := pb.NewUserServiceClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
res, err := client.GetUser(ctx, &pb.UserRequest{Name: "John"})
if err != nil {
log.Fatalf("Could not get user: %v", err)
}
log.Printf("GetUser Response: %s", res.Message)
res, err = client.SetUser(ctx, &pb.UserRequest{Name: "John"})
if err != nil {
log.Fatalf("Could not set user: %v", err)
}
log.Printf("SetUser Response: %s", res.Message)
}
Explanation:
Create Connection – We connect to the gRPC server running on
localhost:50051
.Create Client – We create a new
UserService
client from our generated code.Make Calls – We call
GetUser
andSetUser
with a context that has a timeout, printing the responses.
Conclusion
Now you have a basic gRPC service up and running in Golang! We covered:
Defining a gRPC service using Protocol Buffers.
Setting up and running a gRPC server.
Creating a client to test our gRPC service.
Subscribe to my newsletter
Read articles from Oluwajuwon Falore directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Oluwajuwon Falore
Oluwajuwon Falore
I am a full-Stack (backend leaning) software developer. Experienced with all stages of the development cycle for dynamic web projects. Well-versed in programming languages including HTML5, CSS, JAVASCRIPT, NODEJS, GOLANG, REACTJS, PYTHON, ANGULAR and IONIC.