Introduction to gRPC
This article deep dives into what is gRPC and how to build a client-server app using the gRPC framework.
Today, there are different popular API architectural styles available. For example REST, SOAP, RPC(Remote Procedure Call), GraphQL, etc. Each API architecture style defines the principles of how the different services should communicate using the APIs, how the data exchange must happen, how API error handling must be done, etc.
What is gRPC?
gRPC is an open-source, cross-platform, high-performance Remote Procedure Call (RPC) framework developed by Google. By cross-platform, I meant that it’s not dependent on any single language. It can help you easily generate boilerplate code and help you do fast development for your microservices architecture.
gRPC is built on top of the HTTP/2 communication protocol and uses Protocol buffers(with file extension .proto) for data transfer. The protocol buffers take a small size in data transfer making them fast for data transmission on the network.
In gRPC, a client application can directly call an API of a server application on a different machine as if the client’s application was merely calling another function, making it easier for you to create distributed applications.
For example: in the below image, you can see that the server code is written in C++ and has a gRPC server associated with it. Different clients written in different languages have a gRPC stub and can connect with the server using the proto buffers request and get proto buffers response in return.
Protocol Buffers
When you would have used the REST API style to build your APIs, you most likely used JSON or XML to transfer the data over the network. JSON or XML formats are human-readable formats.
In gRPC, Protocol buffers(commonly called "protobufs") are the data exchange mechanism between the client and the server. Protocol Buffers serialize data in a binary format, making it more compact and faster to parse than human-readable formats like JSON or XML. That’s why, gRPC is such a popular framework to build microservices because of fast & light data transfer over the network.
Build a Golang App using gRPC
Alright, enough about theory. I did one practical example and built a client-server app in Golang using the gRPC framework. I suggest you do the same. This will help you visualize the true power of the gRPC framework. Here are the steps in the Golang:
Step 1: Install the protocol buffer compilers and the extra plugins that are required to generate the automated code using the proto files.
brew install protobuf
protoc --version
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
Refer to this guide for protocol buffer compiler installation.
Step 2: Create a directory, move into it, and initialize it as a go module
mkdir my-go-app
cd my-go-app
go mod init my-go-app
Step 3: Go language install libraries to a bin folder. You need to add the bin folder in the PATH environment so that the protobuf compiler can create the generated files. For example:
export PATH="$PATH:$GOPATH/bin/"
source ~/.zshrc
Step 4: Create the greeting.proto file inside the my-go-app/proto folder.
syntax = "proto3";
option go_package = "./proto";
package greeting;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloResponse) {}
}
message HelloRequest {
string name = 1;
}
message HelloResponse {
string message = 1;
}
Step 5: Run this command to create the generated files.
protoc --go_out=. --go-grpc_out=. proto/greeting.proto
This command would generate the files: greeting.pb.go and greeting_grpc.pb.go. These files contain the boilerplate code for the client and the server interaction.
Step 6: Create the Client and the server files.
server.go
package main
import (
"context"
"fmt"
"log"
"net"
"google.golang.org/grpc"
pb "my-go-app/proto"
)
// Server implements the Greeter service defined in the .proto file.
type server struct {
pb.UnimplementedGreeterServer
}
// SayHello implements the SayHello RPC method.
func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloResponse, error) {
message := fmt.Sprintf("Hello, %s!", req.GetName())
return &pb.HelloResponse{Message: message}, nil
}
func main() {
// Set up a TCP listener on port 1234
lis, err := net.Listen("tcp", ":1234")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
// Create a new gRPC server
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
log.Println("gRPC server is running on port 1234")
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
client.go
package main
import (
"context"
"log"
"time"
"google.golang.org/grpc"
pb "my-go-app/proto"
)
func main() {
// Set up a connection to the server
conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
client := pb.NewGreeterClient(conn)
// Make a request to the server
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
response, err := client.SayHello(ctx, &pb.HelloRequest{Name: "Vivek"})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Client received: %s", response.GetMessage())
}
Step 7: Run the server.go and the client.go and see the magic yourself.
go run server.go
-- Output
2024/11/02 09:30:27 gRPC server is running on port 1234
go run client.go
-- Output
2024/11/02 09:30:31 Client received: Hello, Vivek!
Takeaways
Now that you’ve built a full-fledged client-server using Golang, you can observe the following points about using the gRPC framework:
Did you notice how greeting.proto defines an API contract which is important for all clients to communicate with the server? The proto files act as the single source of truth for client-server communication.
The proto file is written generically and not constrained to any particular language.
In this example, we used the client and server both in Golang, but that does not demonstrate the gRPC framework’s full power. You can create your server in C++ using a similar proto file and host it in a different repository. Similarly, you can create a client in Java and mention the C++ service’s proto file as the dependency (just like other language dependencies) and use it for calling the server.
That’s it, folks for this edition of the newsletter. I hope you liked this edition. I will continue to write more about gRPC and Golang in the future editions.
Please consider liking 👍 and sharing with your friends as it motivates me to bring you good content for free. If you think I am doing a decent job, share this article in a nice summary with your network. Connect with me on Linkedin or Twitter for more technical posts in the future!
Resources
Introduction to gRPC
What are protocol buffers?
Protocol Buffers Basics Go
gRPC basics tutorial in Golang
Great article
Interesting article.