Build client-server application using gRPC

A quick help on how to create and execute a simple client-server application using gRPC

Photo by Alexandre Debiève on Unsplash

Introduction

gRPC is a an open source RPC (Remote Procedure Call) framework that can be used to create high performance distributed applications and services. If you would like to have an introduction to gRPC, refer to my earlier article here, which gives a brief introduction to gRPC, it’s features and suitable usecases.

In this article, we’ll cover how to create and execute a simple client-server application using gRPC.

Target Audience

This article is intended for those who are relatively new to gRPC, but, may have some experience with Go programming language. If you are already familiar with gRPC, well, feel free to glance through. :-)

What are we building?

To keep things simple, let’s go with a very basic example: a service to search whether a product is in stock or not. Needless to say, we’ll focus only on the gRPC aspects of it and leave out rest of the logic as future scope. In short, nothing fancy :-)

We’ll define the service, create both server and client version of the application using Go programming language.

Prerequisites

Since we are planning to use Go, ensure that Go programming language installed and configured in your machine. If not done already, no worries; Feel free to refer my earlier article which explains step by step on how to setup up local development environment.

Another prerequisite we have is Protocol Buffers (version 3 i.e. proto3). To configure the same, download pre-compiled binary from here. You could choose a binary which is relevant for your platform: (e.g.: protoc-3.17.0-linux-x86_64.zip)

Unzip the file and keep it in a relevant directory. You would be able to see the compiler file (protoc) in the directory. Include the same in PATH.

Run the following to see if the configuration is correct.

protoc version

You should be able to see something similar:

protoc version

In addition to Protocol Buffers compiler, we also need to install the following Go plugins

$ go install google.golang.org/protobuf/cmd/protoc-gen-go
$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc

Above statements will install the Go plugins to GOPATH/bin location. Ensure that GOPATH/bin is added to PATH variable.

On a Linux machine, you could do it as follows:

$ $export PATH=”$PATH:$(go env GOPATH)/bin”

Project Structure

Now, let’s look at how to structure our project.

Our project will have a service definition, it’s supporting files, implementation files for client & server and, a main file to start the program. (In ideal case, the client implementation would be kept separate from server implementation. To keep things simple, we’ll keep all relevant parts in the same project itself.)

When completed, our project structure would look like the following.

project structure

Now, let’s create a Go project (say grpc_go_example) with modules enabled. Create directories to keep client, server and service definition as shown above.

Service Definition

Fundamental building block for gRPC is the service definition. Let’s start with defining our service as follows.

Let’s try to understand what these statements mean.

First line in the file is to specify which version of Protocol Buffers we would use, followed by go package details. Next, a service is defined along with message structure for both request and response. Save the file as product.proto in protof directory.

Now, we need to compile the .proto file to generate it’s supporting files.

Execute the following from the project directory

$ protoc — go_out=. — go_opt=paths=source_relative — go-grpc_out=. — go-grpc_opt=paths=source_relative protof/product.proto

After executing this, you would be able to see two additional files (product_grpc.pb.go and product.pb.go) generated in the protof directory. We’ll use these files to implement both server and client programs.

protof directory

Server Implementation

It’s time to implement the server now.

First, let’s define the package and import a few required packages. Note that, we need to import the package where our service definition files are kept as well. Let’s also specify a port to use.

Next step is to implement the server. To do so, let’s define a struct called server and implement protof.ProductServer interface (present in product_grpc.pb.go file). We’ll add a simple response logic in Search() method as indicated.

Now, let’s add an entry point as follows, which would be used by main function, to invoke server.

Now, the server implementation is ready.

Client Implementation

Let’s proceed to implement client.

Similar to server implementation, let’s define the package, import necessary packages and define the address. To simplify, we’ll also add a constant for default product.

Next step is to add an entry point as follows, which would be used by main function, to invoke client.

Now, we are done with client implementation.

Main Program

Now, it’s time to create the final piece in the puzzle, the main program.

Here, we’ll keep the package as main and import both client and server packages.

In the main function, we’ll expect an argument (client or server) and accordingly, respective function would be called as follows.

Execution

Now, it’s time to run the program.

To execute the server and client, run the commands as shown below, in sequence in two different terminals.

terminal-1: start the server
terminal-2: run client with default input

As you can see above, server has responded back with the result.

terminal-1: server logs

Since we did not specify any product, client send the default product we specified.

Let’s try running the client with another product as an argument, in another terminal.

terminal-3: run client with another input
terminal-1: server logs

Since this product is not configured, server responds back with “not in stock” message.

Source Code

If you would like to experiment, working version of the program can be found in the following GitHub repo:

https://github.com/bijeshos/grpc-go-example

That’s all for now.

Thank you reading so far. Till we meet next time, happy coding!