gRPC with Golang and Python

Anderson Borges
5 min readMar 19, 2019

In this article we will create a small application using gRPC to create a communication between Golang and Python, but first we need understand what gRPC is.

gRPC is short for Google’s Remote Procedure Call, is an open source RPC framework used to call code that is running remotely, gRPC uses Client-server architecture which many clients request and receive service from a centralized server. In that way, clients and servers can talk to each other independently of the environments. gRPC uses HTTP/2 for transport and protocol buffers (a binary serialization format that is very small and quick to encode and decode) for serializing structured data.

First, we need to install the Protocol Buffer https://github.com/protocolbuffers/protobuf/releases

Once the Protocol Buffer is ready, we’ll need to add a plugin that will allow us to work with Go code. In a terminal, run a “go get” command:

go get -u github.com/golang/protobuf/{proto,protoc-gen-go}
go get -u google.golang.org/grpc

Our project is going to be a simple application the receive a country name and returns information about the country [Name, Capital, Population, Currencies].

Let’s start coding

Let’s begin creating a new folder in $GOPATH/src/ called grpc, this folder is going to be a root folder to the projects and 2 subfolders called server and client inside the grpc folder. The server directory is going to have the Golang code and client directory Python Code.

mkdir $GOPATH/src/grpc
mkdir $GOPATH/src/grpc/{client,server}

In order to create the gRPC we need to start creating a .proto file, this file is going to contain the protocol buffer data signature, I am naming as countries.proto in the root directory.

#$GOPATH/src/grpc/countries.proto

syntax = "proto3";
package countries;
service Country {
rpc Search (CountryRequest) returns (CountryResponse) {}
}
message CountryRequest {
string name = 1;
}
message Currencies {
string code = 1;
string name = 2;
string symbol = 3;
}
message CountryResponse {
string name = 1;
string alpha2Code = 2;
string capital = 3;
string subregion = 4;
int32 population = 5;
string nativeName = 6;
repeated Currencies currencies = 7;
}

The important thing we specify in the .proto is that we are using the “proto3” version, for more detail just read the docs proto3 and proto2.

The service rpc block has one method “Search” that must be implemented and return the ‘CountryResponse’.

Note the Go or Python doesn’t understand proto3 code, to work with those languages you have to compile the proto file, that’s why we have installed the “protoc-gen-go”.

Creating Go code from .proto

cd $GOPATH/src/grpc/
mkdir $GOPATH/src/grpc/server/countries
protoc --go_out=plugins=grpc:server/countries countries.proto

After running the command you will see there are one file “countries.pb.go” inside server/countries folder, feel free to open the file and check its contents. A bunch of code is generated and you shouldn’t modify this code if for any reason you have to change something, you change in the .proto and then compiles again.

Keep in mind that does not matter which language is going to be the .proto is the same and each language plugin has its own output.

Building the server

touch $GOPATH/src/grpc/server/main.go
touch $GOPATH/src/grpc/server/main_test.go

# $GOPATH/src/grpc/server/main.go

package mainimport (
"encoding/json"
"grpc/server/countries"
"io/ioutil"
"log"
"net"
"net/http"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
func main() { grpcServer := grpc.NewServer() var server Server countries.RegisterCountryServer(grpcServer, server) listen, err := net.Listen("tcp", "0.0.0.0:3000") if err != nil {
log.Fatalf("could not listen to 0.0.0.0:3000 %v", err)
}
log.Println("Server starting...")
log.Fatal(grpcServer.Serve(listen))
}
// Server is implementation proto interface
type Server struct{}
// Search function responsible to get the Country information
func (Server) Search(ctx context.Context, request *countries.CountryRequest) (*countries.CountryResponse, error) {
resp, err := http.Get("https://restcountries.eu/rest/v2/name/ " + request.Name) if err != nil {
return nil, err
}
defer resp.Body.Close()
jsonData, err := ioutil.ReadAll(resp.Body) if err != nil {
return nil, err
}
var data []countries.CountryResponse
if err := json.Unmarshal(jsonData, &data); err != nil {
return nil, err
}
return &data[0], nil
}

Probably you are thinking why I am using a list of CountryResponse to serializer the data, I do this because the api rest countries return a list of country not a single country.

Run the server

go run main.go

Test is important

# $GOPATH/src/grpc/server/main_test.go

package mainimport (
"grpc/server/countries"
"log"
"testing"
"golang.org/x/net/context"
)
func TestCountry(t *testing.T) {
ctx := context.Background()
request := countries.CountryRequest{Name: "Brazil"}
server := Server{}
response, err := server.Search(ctx, &request)
if err != nil {
t.Error(err)
}
if response.Alpha2Code != "BR" {
t.Error("Different Country returned")
}
log.Println(response)
}

inside the server folder run the command “go test” you should get a message like this “PASS — ok grpc/server 1.001s”

Creating Python code from .proto

Now that we have the gRPC server running and tested we going to create the client using Python. Imagine that you don’t have any contact with the gRPC server the only file that you have is the countries.proto, in fact, you do not know which language the server was built.

To start working with python we are going to create a virtualenv project, virtualenv is a tool to create isolated Python environments. creates a folder which contains all the necessary executables to use the packages that a Python project would need. Here a tutorial showing how to install virtualenv.

The virtualenv project does not need to be inside the Project.

virtualenv -p python3.6 /tmp/country

For now, let create inside the /tmp directory. To start using the virtual environment, it needs to be activated.

source /tmp/country/bin/activate

As we did in Go we have to install the plugins, packages and compile the countries.proto file in Python as well.

pip install grpcio grpcio-tools

If you run “pip freeze” you should see something like this:

grpcio==1.19.0
grpcio-tools==1.19.0
protobuf==3.7.0

Creating Python code from the countries.proto:

cd $GOPATH/src/grpc/
python -m grpc_tools.protoc -I. --python_out=client --grpc_python_out=client countries.proto

The command will create 2 files in client folder “countries_pb2_grpc.py” and “countries_pb2.py”

The python client file:

touch $GOPATH/src/grpc/client/app.py

We can set this client to run a Python script or as a web application, how we run is not important as long as we have to connect to the gRPC server. To create a web application let’s install Flask as the web framework

pip install Flask

#$GOPATH/src/grpc/client/app.py

from flask import Flask
from flask import jsonify
app = Flask(__name__)import grpc
import countries_pb2
import countries_pb2_grpc
def obj_to_dict(obj):return obj.__dict__@app.route('/<countrie>')
def countrie(countrie):
print("Start service")
try:
channel = grpc.insecure_channel('localhost:3000')
stub = countries_pb2_grpc.CountryStub(channel)
countryRequest = countries_pb2.CountryRequest(name=countrie)
countryResponse = stub.search(countryRequest)
return jsonify({
"name": countryResponse.name,
"alpha2Code": countryResponse.alpha2Code,
"capital": countryResponse.capital,
"subregion": countryResponse.subregion,
"population": countryResponse.population,
"nativeName": countryResponse.nativeName
})
except Exception as e:
print(e)
return e
if __name__ == '__main__':
app.run()

Running the client, don’t forget to start the gRPC server first.

python app.pycurl http://localhost:5000/brazil
More country names to test:
germany
afghanistan
albania
colombia

We covered a few things and we put together a simple example of gRPC, we learned how gRPC give us efficient transport communications between clients and servers regardless of the language used.

├── client
│ ├── app.py
│ ├── countries_pb2_grpc.py
│ ├── countries_pb2.py
├── countries.proto
└── server
├── countries
│ └── countries.pb.go
├── main.go
└── main_test.go

Source Code

https://bitbucket.org/burdzand/grpc

Links

--

--