In this post we are going to build a simple gRPC client and server application. I was recently trying to set this up in one of my projects and was struggling to do it. Hence I decided to write a post which focuses only on the exact steps to setup gRPC communication between server and client written in Golang.
I will not cover the basics of gRPC and why we should use it. If you are new to it you can read about it here. Before we start here are the pre-requisites:
Create project directory and initialise Go modules
Don’t worry if you are unaware with some of the files in below tree structure, for now just create empty files / directories using the given commands.
$ mkdir exchange-app
$ cd exchange-app
$ go mod init bank/exchange-app
$ mkdir proto
$ touch server.go client.go proto/currency_conversion.proto
$ tree .
.
├── client.go
├── go.mod
├── proto
│ └── currency_conversion.proto
└── server.go
Install gRPC and protoc-gen-go
$ go get -u google.golang.org/grpc
$ go get -u github.com/golang/protobuf/protoc-gen-go
Install protobuf
# Linux
$ apt install -y protobuf-compiler
$ protoc --version
# Mac OS
$ brew install protobuf
$ protoc --version
For other operating systems, please refer this link.
Let’s setup the client and server communication
Define a service in a .proto file
The first step is to complete the currency_conversion.proto
file which defines the gRPC service and the request and response messages using protocol buffers. Now open the currency_conversion.proto
file in a text editor and paste the below content in it.
// currency_conversion.proto
// this file will be used to generate the grpc client and server interfaces in Go
syntax = "proto3";
package proto;
message CurrencyConversionRequest {
string from = 1;
string to = 2;
float value = 3;
}
message CurrencyConversionResponse {
float converted_value = 1;
}
service CurrencyConversionService {
rpc Convert(CurrencyConversionRequest) returns (CurrencyConversionResponse) {}
}
Generate server and client code using the protocol buffer compiler
From the project root directory, run the below command to generate the gRPC client and server interfaces in Go which can be used as a Go package in the client and server code.
$ protoc -I proto/ proto/currency_conversion.proto --go_out=plugins=grpc:proto/
After executing the above command, if we again look at the tree structure of the current directory, we can see that a currency_conversion.pb.go
file is generated.
$ tree .
.
├── client.go
├── go.mod
├── proto
│ ├── currency_conversion.pb.go // generated file
│ └── currency_conversion.proto
└── server.go
The generated file contains the following contents:
- Golang struct type for each message defined in
currency_conversion.proto
file. It also has helper methods to get value of each field defined in the struct. As an example, look at the generated code for proto message CurrencyConversionRequest
type CurrencyConversionRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
From string `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"`
To string `protobuf:"bytes,2,opt,name=to,proto3" json:"to,omitempty"`
Value float32 `protobuf:"fixed32,3,opt,name=value,proto3" json:"value,omitempty"`
}
func (x *CurrencyConversionRequest) GetFrom() string {
if x != nil {
return x.From
}
return ""
}
func (x *CurrencyConversionRequest) GetTo() string {
if x != nil {
return x.To
}
return ""
}
func (x *CurrencyConversionRequest) GetValue() float32 {
if x != nil {
return x.Value
}
return 0
}
- An interface type which clients will use to call the methods implemented by the grpc server
type CurrencyConversionServiceClient interface {
Convert(ctx context.Context, in *CurrencyConversionRequest, opts ...grpc.CallOption) (*CurrencyConversionResponse, error)
}
type currencyConversionServiceClient struct {
cc grpc.ClientConnInterface
}
func NewCurrencyConversionServiceClient(cc grpc.ClientConnInterface) CurrencyConversionServiceClient {
return ¤cyConversionServiceClient{cc}
}
func (c *currencyConversionServiceClient) Convert(ctx context.Context, in *CurrencyConversionRequest, opts ...grpc.CallOption) (*CurrencyConversionResponse, error) {
out := new(CurrencyConversionResponse)
err := c.cc.Invoke(ctx, "/currency_conversion.CurrencyConversionService/Convert", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
- An interface type which the servers will implement to serve the client requests
// CurrencyConversionServiceServer is the server API for CurrencyConversionService service.
type CurrencyConversionServiceServer interface {
Convert(context.Context, *CurrencyConversionRequest) (*CurrencyConversionResponse, error)
}
// this is the function servers will use to register their struct object which implements the grpc service methods
func RegisterCurrencyConversionServiceServer(s *grpc.Server, srv CurrencyConversionServiceServer) {
s.RegisterService(&_CurrencyConversionService_serviceDesc, srv)
}
Finish the server.go file
Now let’s implement the server.go
package main
import (
"bank/exchange-app/proto"
"context"
"fmt"
"net"
"google.golang.org/grpc"
)
// myGRPCServer implements CurrencyConversionServiceServer interface
type myGRPCServer struct {
}
// actual implementation of the Convert method
func (s *myGRPCServer) Convert(ctx context.Context, request *proto.CurrencyConversionRequest) (*proto.CurrencyConversionResponse, error) {
fmt.Printf("request received to convert currency %+v \n", request)
// NOTE: here you can write the logic to convert the currency
return &proto.CurrencyConversionResponse{
ConvertedValue: 74.02,
}, nil
}
func main() {
fmt.Println("starting gRPC server application")
// start a listener on the port you want to start the grpc server
lis, err := net.Listen("tcp", ":9000")
if err != nil {
fmt.Println(err)
return
}
// create a grpc server object
srv := grpc.NewServer()
// pass the address of the struct which implements the CurrencyConversionServiceServer interface
proto.RegisterCurrencyConversionServiceServer(srv, &myGRPCServer{})
// start the grpc server
err = srv.Serve(lis)
if err != nil {
return
}
return
}
But what have we done here ? Let’s see what steps can be followed to write the server.go code
- Create a struct (give it any name you want). It should implement the
CurrencyConversionServiceServer
interface
// myGRPCServer implements CurrencyConversionServiceServer interface
type myGRPCServer struct {
}
// actual implementation of the Convert method
func (s * myGRPCServer) Convert(ctx context.Context, request *proto.CurrencyConversionRequest) (*proto.CurrencyConversionResponse, error) {
fmt.Printf("request received to convert currency %+v \n", request)
// NOTE: here you can write the logic to convert the currency
return &proto.CurrencyConversionResponse{
ConvertedValue: 74.02,
}, nil
}
2. Start a listener on 9000 port (you can use port of your choice)
lis, err := net.Listen("tcp", ":9000")
if err != nil {
fmt.Println(err)
return
}
3. Using google.golang.org/grpc
package’s NewServer method, create a new grpc server object. It creates a gRPC server object which has no service registered and has not started to accept requests yet
srv := grpc.NewServer()
4. Register the defined struct to inform grpc about the object which implements the grpc server
proto.RegisterCurrencyConversionServiceServer(srv, & myGRPCServer{})
5. Start the gRPC server now which will listen for incoming requests at port 9000
// start the grpc server
err = srv.Serve(lis)
if err != nil {
return
}
Finish the client.go file
Here is the implemented grpc client.go file and you can find the breakdown of each step after it.
package main
import (
"context"
"fmt"
"bank/exchange-app/proto"
"google.golang.org/grpc"
)
func main() {
fmt.Println("starting grpc client application")
// use grpc.Dial to create a grpc connection to running grpc server
conn, err := grpc.Dial(":9000", grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
fmt.Println(err)
return
}
defer conn.Close()
// use the NewCurrencyConversionServiceClient function defined in generated .pb.go file
// to generate a grpc client object
// this object can be used to call methods implemented in grpc server
client := proto.NewCurrencyConversionServiceClient(conn)
// create the grpc request
request := &proto.CurrencyConversionRequest{
From: "INR",
To: "USD",
Value: 1,
}
// call the grpc server method Convert using the client
response, err := client.Convert(context.Background(), request)
if err != nil {
fmt.Println(err)
return
}
// print the response
fmt.Println("converted currency value is: ", response.GetConvertedValue())
}
- Dial a connection to the server
// use grpc.Dial to create a grpc connection to running grpc server
conn, err := grpc.Dial(":9000", grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
fmt.Println(err)
return
}
2. Create a grpc client object using the connection object. This object will be used to call server methods
client := proto.NewCurrencyConversionServiceClient(conn)
3. Create the grpc request and call the server methods
// create the grpc request
request := &proto.CurrencyConversionRequest{
From: "INR",
To: "USD",
Value: 1,
}
// call the grpc server method Convert using the client
response, err := client.Convert(context.Background(), request)
if err != nil {
fmt.Println(err)
return
}
And that’s it.
Time to test
- Run the GRPC server
$ go run server.go
starting gRPC server application
2. Run the client
$ go run client.go
starting grpc client application
converted currency value is: 74.02
When you run the client code, server will also show the log message
request received to convert currency from:"INR" to:"USD" value:1
That was it! I hope you enjoyed the post and learned how we can establish gRPC communication between client and server written in Golang. If you enjoyed the post, please like and share. Any feedbacks or suggestions will encourage me to write more. Thanks for reading!
Like this:
Like Loading...
Anuj Verma
Share post:
In this post we are going to build a simple gRPC client and server application. I was recently trying to set this up in one of my projects and was struggling to do it. Hence I decided to write a post which focuses only on the exact steps to setup gRPC communication between server and client written in Golang.
I will not cover the basics of gRPC and why we should use it. If you are new to it you can read about it here. Before we start here are the pre-requisites:
Create project directory and initialise Go modules
Don’t worry if you are unaware with some of the files in below tree structure, for now just create empty files / directories using the given commands.
Install gRPC and protoc-gen-go
Install protobuf
For other operating systems, please refer this link.
Let’s setup the client and server communication
Define a service in a .proto file
The first step is to complete the
currency_conversion.proto
file which defines the gRPC service and the request and response messages using protocol buffers. Now open thecurrency_conversion.proto
file in a text editor and paste the below content in it.Generate server and client code using the protocol buffer compiler
From the project root directory, run the below command to generate the gRPC client and server interfaces in Go which can be used as a Go package in the client and server code.
After executing the above command, if we again look at the tree structure of the current directory, we can see that a
currency_conversion.pb.go
file is generated.The generated file contains the following contents:
currency_conversion.proto
file. It also has helper methods to get value of each field defined in the struct. As an example, look at the generated code for protomessage CurrencyConversionRequest
Finish the server.go file
Now let’s implement the server.go
But what have we done here ? Let’s see what steps can be followed to write the server.go code
CurrencyConversionServiceServer
interface2. Start a listener on 9000 port (you can use port of your choice)
3. Using
google.golang.org/grpc
package’s NewServer method, create a new grpc server object. It creates a gRPC server object which has no service registered and has not started to accept requests yet4. Register the defined struct to inform grpc about the object which implements the grpc server
5. Start the gRPC server now which will listen for incoming requests at port 9000
Finish the client.go file
Here is the implemented grpc client.go file and you can find the breakdown of each step after it.
2. Create a grpc client object using the connection object. This object will be used to call server methods
3. Create the grpc request and call the server methods
And that’s it.
Time to test
2. Run the client
When you run the client code, server will also show the log message
That was it! I hope you enjoyed the post and learned how we can establish gRPC communication between client and server written in Golang. If you enjoyed the post, please like and share. Any feedbacks or suggestions will encourage me to write more. Thanks for reading!
Share this:
Like this: