gRPC and Protocol Buffers in Golang

Why do we need RPCs?

RPC - Remote Procedure Call

Image source: geeksforgeeks.org
Image source: geeksforgeeks.org

gRPC

Image source: www.grpc.io

Protobuf - Protocol Buffers

{
first_name: "Swagat",
last_name: "Parida",
roll_no: "16EE01025",
Role: "Student"
}
<?xml version="1.0" encoding="UTF-8"?> <root>    <Role>Student</Role>  
<first_name>Swagat</first_name>
<last_name>Parida</last_name>
<roll_no>16EE01025</roll_no>
</root>
message msg {
string first_name = 0
string last_name = 1
string roll_no = 2
string Role = 3
}
026Swagat126Parida22816EE01025327Student
syntax = "proto3";message msg{
string first_name = 0;
string last_name = 1;
string roll_no = 2;
enum Role{
STUDENT = 0;
TEACHER = 1;
OTHER = 2;
}
reserved keyword is used to prevent a tag from being redefined.

Hands On

For Mac

brew install protobuf

For Ubuntu

sudo apt install protobuf-compiler
protoc --versions
go get -u google.golang.org/grpc
go get -u github.com/golang/protobuf/protoc-gen-go
syntax = "proto3";
package proto;
message Request {
int64 a = 1;
int64 b = 2;
}
message Response {
int64 result = 1; }
service AddService {
rpc Add(Request) returns (Response);
rpc Multiply(Request) returns (Response);
}
protoc --go_out=plugins=grpc:proto proto/service.proto
type Request struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields

A int64 `protobuf:"varint,1,opt,name=a,proto3" json:"a,omitempty"`
B int64 `protobuf:"varint,2,opt,name=b,proto3" json:"b,omitempty"`
}
type Response struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields

Result int64 `protobuf:"varint,1,opt,name=result,proto3" json:"result,omitempty"`
}
type AddServiceClient interface {
Add(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
Multiply(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
}
package main

import (
"fmt"
"grpc_tutorial/proto"
"log"
"net/http"
"strconv"

"github.com/gin-gonic/gin"
"google.golang.org/grpc"
)

func main() {
conn, err := grpc.Dial("localhost:4040", grpc.WithInsecure())
if err != nil {
panic(err)
}

client := proto.NewAddServiceClient(conn)

g := gin.Default()
g.GET("/add/:a/:b", func(ctx *gin.Context) {
a, err := strconv.ParseUint(ctx.Param("a"), 10, 64)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Parameter A"})
return
}

b, err := strconv.ParseUint(ctx.Param("b"), 10, 64)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Parameter B"})
return
}

req := &proto.Request{A: int64(a), B: int64(b)}
if response, err := client.Add(ctx, req); err == nil {
ctx.JSON(http.StatusOK, gin.H{
"result": fmt.Sprint(response.Result),
})
} else {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
}
})

g.GET("/mult/:a/:b", func(ctx *gin.Context) {
a, err := strconv.ParseUint(ctx.Param("a"), 10, 64)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Parameter A"})
return
}
b, err := strconv.ParseUint(ctx.Param("b"), 10, 64)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Parameter B"})
return
}
req := &proto.Request{A: int64(a), B: int64(b)}

if response, err := client.Multiply(ctx, req); err == nil {
ctx.JSON(http.StatusOK, gin.H{
"result": fmt.Sprint(response.Result),
})
} else {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
}
})

if err := g.Run(":8080"); err != nil {
log.Fatalf("Failed to run server: %v", err)
}

}
package main

import (
"context"
"grpc_tutorial/proto"
"net"

"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)

type server struct{}

func main() {
listener, err := net.Listen("tcp", ":4040")
if err != nil {
panic(err)
}

srv := grpc.NewServer()
proto.RegisterAddServiceServer(srv, &server{})
reflection.Register(srv)

if e := srv.Serve(listener); e != nil {
panic(e)
}

}

func (s *server) Add(ctx context.Context, request *proto.Request) (*proto.Response, error) {
a, b := request.GetA(), request.GetB()

result := a + b

return &proto.Response{Result: result}, nil
}

func (s *server) Multiply(ctx context.Context, request *proto.Request) (*proto.Response, error) {
a, b := request.GetA(), request.GetB()

result := a * b

return &proto.Response{Result: result}, nil
}
go run server/main.go
go run client/main.go
localhost:8080/add/32/32
localhost:8080/mult/3/2112

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store