gRPC in Python. Part 1: Building a gRPC server

Building fast and scalable APIs using gRPC

Code

The code for this article is available at: https://github.com/theundeadmonk/python-grpc-demo/

This is part of a series of gRPC in python. We will cover the following

1) Implementing a server

2) Implementing a client

3) gRPC Streaming

4) Advanced features like interceptors, etc.

5) Serving frontends using gRPC gateway

What is gRPC

[gRpc](https://grpc.io/) is a high-performance RPC framework developed by Google. Google has thousands of microservices running in its data centers and they built gRPC as a means of communicating between all of these them.

Why gRPC

Most organizations will use REST APIs to communicate between their microservices. As organizations scale, this does have a few downsides. REST APIs do not enforce a strict schema. This means that it is hard to coordinate changes between servers and clients, and although tools such as OpenAPI and JSONSchema exist, they are an afterthought and are often cumbersome in practice. Using Protobufs, gRPC forces clients and servers to agree on the API contract, and protobufs do not allow for breaking changes.

gRPC also uses HTTP/2 as a transport mechanism. This offers several benefits over HTTP/1.1 such as multiplexing requests, etc.

Enough about the benefits of gRPC, let's see how to implement a gRPC server in Python.

Service Definition

Every gRPC service begins with a proto file. This file is where we define our gRPC service and its types. For more information on protocol buffers, checkout this tutorial here: https://developers.google.com/protocol-buffers/docs/pythontutorial

Let us define a simple gRPC service. It has a service called Greeter. Let us call this file greet.proto

syntax = "proto3";
package tutorial;

message GreetingRequest {
    string name = 1;
}

message GreetingResponse {
    string greeting = 1;
}

service Greeter {
  rpc Greet(GreetingRequest) returns (GreetingResponse );
}

What we have here is a simple proto file. It defines a single service called Greeter that takes in a GreetingRequest and returns a GreetingResponse. Protocol buffers are strongly typed and we need to create types for all services that we define. The type GreetingReuest contains a string called name and the type GreetignResponse contains a string called greeting.

Server

Now that we have our protobuf definition, let's start creating our server in Python. We'll be using poetry to manage our dependencies, but you can use any dependency management tool you like. Let's initialize our project and install the required dependencies.

poetry new python-grpc-demo
poetry add grpcio
poetry add grpcio-tools

Generating types

The good thing about using gRPC and Protobufs is that we get automatic code generation for our services. In order to do this, we use a tool called [protoc](https://grpc.io/docs/protoc-installation/) which generates the required python files.

Let's create two directories in our project protos and grpc_types . We should also place the greeting.proto file in the protos directory.

Our directory structure should now look like the following

├── poetry.lock
├── pyproject.toml
├── python_grpc_demo
│   ├── grpc_types
│   │   └── __init__.py
│   ├── __init__.py
│   └── protos
│       ├── greeting.proto
│       └── __init__.py
├── README.rst
└── tests
    ├── __init__.py
    └── test_python_grpc_demo.py

Let us now use the protoc command to generate out python code. protoc comes bundled with the grpcio-tools package that we installed earlier so let's invoke it using the following command.

$ poetry run python -m grpc_tools.protoc --proto_path=./python_grpc_demo/protos --python_out=./python_grpc_demo/grpc_types --pyi_out=./python_grpc_demo/grpc_types --grpc_python_out=./python_grpc_demo/grpc_types ./python_grpc_demo/protos/greeting.proto

This should generate the following files for us in the grpc_types directory.

greeting_pb2_grpc.py

greeting_pb2.py

greeting_pb2.pyi

Open the greeting_pb2_grpc.py file and edit the following line

import greeting_pb2 as greeting__pb2 to be

import python_grpc_demo.grpc_types.greeting_pb2 as greeting__pb2

Implementing the service

Let us now go ahead and implement our service. Since we already have code generated from the previous step, we can go ahead and use that to implement our gRPC service.

Let us create a new directory structure called server/servicers and within it, a file called greeter.py. Implement a basic grpc service that returns a greeting

import grpc

from python_grpc_demo.grpc_types.greeting_pb2_grpc import GreeterServicer
from python_grpc_demo.grpc_types.greeting_pb2 import GreetingRequest, GreetingResponse

class Greeter(GreeterServicer):
    def Greet(self, request: GreetingRequest, context: grpc.ServicerContext) -> GreetingResponse:
        return GreetingResponse(greeting='Hello %s', request.name)

Creating the server.

Now that we have the gRPC service implemented, let us go ahead and create a server to serve our clients. Create a file called server.py in the server folder and add the following code to it.

import grpc
from concurrent import futures

from python_grpc_demo.grpc_types.greeting_pb2_grpc import add_GreeterServicer_to_server
from python_grpc_demo.server.servicers.greeter import Greeter


def serve() -> None:
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    add_GreeterServicer_to_server(Greeter(), server)
    port = 50051
    server.add_insecure_port(f"[::]:{port}")
    server.start()
    print("Server started")
    server.wait_for_termination()

What we've done here is simply created a gRPC server

server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))

Added our service to it

add_GreeterServicer_to_server(Greeter(), server)

And started service on port 50051.

Modify your pyproject.toml file and add the following lines

[tool.poetry.scripts]
run-grpc-server = "python_grpc_demo.server:serve"

We can now start our server with the following command

$ poetry run run-grpc-server
Server started

That's it! Our server is now ready to serve clients at port 50051.

Next week, we'll see how to create a client to consume our gRPC service.