Primeiros passos com gRPC-Python

1. Introdução

Neste codelab, você vai usar o gRPC-Python para criar um cliente e um servidor que formam a base de um aplicativo de mapeamento de rotas escrito em Python.

Ao final do tutorial, você terá um cliente que se conecta a um servidor remoto usando o gRPC para receber o nome ou o endereço postal do que está localizado em coordenadas específicas em um mapa. Um aplicativo completo pode usar esse design cliente-servidor para enumerar ou resumir pontos de interesse ao longo de um trajeto.

O serviço é definido em um arquivo Protocol Buffers, que será usado para gerar código boilerplate para o cliente e o servidor, permitindo que eles se comuniquem entre si e economizando tempo e esforço na implementação dessa funcionalidade.

Esse código gerado cuida não apenas das complexidades da comunicação entre o servidor e o cliente, mas também da serialização e desserialização de dados.

O que você vai aprender

  • Como usar buffers de protocolo para definir uma API de serviço.
  • Como criar um cliente e um servidor baseados em gRPC com uma definição de buffers de protocolo usando a geração automática de código.
  • Entendimento da comunicação cliente-servidor com gRPC.

Este codelab é destinado a desenvolvedores Python que não conhecem o gRPC ou querem relembrar o assunto, ou a qualquer pessoa interessada em criar sistemas distribuídos. Não é necessário ter experiência com gRPC.

2. Antes de começar

O que é necessário

  • Python 3.9 ou mais recente. Recomendamos o Python 3.13. Para instruções de instalação específicas da plataforma, consulte Configuração e uso do Python. Se preferir, instale um Python não sistêmico usando ferramentas como uv ou pyenv.
  • pip para instalar pacotes Python.
  • venv para criar ambientes virtuais em Python.

Os pacotes ensurepip e venv fazem parte da biblioteca padrão do Python e geralmente estão disponíveis por padrão.

No entanto, algumas distribuições baseadas em Debian (incluindo o Ubuntu) optam por excluí-los ao redistribuir o Python. Para instalar os pacotes, execute:

sudo apt install python3-pip python3-venv

Acessar o código

Para facilitar o aprendizado, este codelab oferece um scaffold de código-fonte pré-criado para ajudar você a começar. As etapas a seguir vão orientar você na conclusão do aplicativo, incluindo a geração de código gRPC usando o plug-in do compilador de buffer de protocolo grpc_tools.protoc.

grpc-codelabs

O código-fonte do scaffold para este codelab está disponível no diretório codelabs/grpc-python-getting-started/start_here. Se preferir não implementar o código por conta própria, o código-fonte concluído está disponível no diretório completed.

Primeiro, crie o diretório de trabalho do codelab e use cd para acessar ele:

mkdir grpc-python-getting-started && cd grpc-python-getting-started

Faça o download e extraia o codelab:

curl -sL https://github.com/grpc-ecosystem/grpc-codelabs/archive/refs/heads/v1.tar.gz \
  | tar xvz --strip-components=4 \
  grpc-codelabs-1/codelabs/grpc-python-getting-started/start_here

Como alternativa, baixe o arquivo .zip que contém apenas o diretório do codelab e descompacte-o manualmente.

3. Definir o serviço

A primeira etapa é definir o serviço gRPC do aplicativo, o método RPC e os tipos de mensagens de solicitação e resposta usando a linguagem de definição de interface Protocol Buffers. Seu serviço vai oferecer:

  • Um método RPC chamado GetFeature que o servidor implementa e o cliente chama.
  • Os tipos de mensagem Point e Feature são estruturas de dados trocadas entre o cliente e o servidor ao usar o método GetFeature. O cliente fornece coordenadas do mapa como um Point na solicitação GetFeature ao servidor, e o servidor responde com um Feature correspondente que descreve o que está localizado nessas coordenadas.

Esse método RPC e os tipos de mensagem dele serão definidos no arquivo protos/route_guide.proto do código-fonte fornecido.

Os buffers de protocolo são conhecidos como protobuf. Para mais informações sobre a terminologia do gRPC, consulte Conceitos principais, arquitetura e ciclo de vida do gRPC.

Tipos de mensagem

No arquivo protos/route_guide.proto do código-fonte, primeiro defina o tipo de mensagem Point. Um Point representa um par de coordenadas de latitude e longitude em um mapa. Neste codelab, use números inteiros para as coordenadas:

message Point {
  int32 latitude = 1;
  int32 longitude = 2;
}

Os números 1 e 2 são IDs exclusivos para cada um dos campos na estrutura message.

Em seguida, defina o tipo de mensagem Feature. Um Feature usa um campo string para o nome ou endereço postal de algo em um local especificado por um Point:

message Feature {
  // The name or address of the feature.
  string name = 1;

  // The point where the feature is located.
  Point location = 2;
}

Método de serviço

O arquivo route_guide.proto tem uma estrutura service chamada RouteGuide que define um ou mais métodos fornecidos pelo serviço do aplicativo.

Adicione o método rpc GetFeature à definição RouteGuide. Como explicado anteriormente, esse método vai pesquisar o nome ou endereço de um local em um determinado conjunto de coordenadas. Portanto, faça com que GetFeature retorne um Feature para um determinado Point:

service RouteGuide {
  // Definition of the service goes here

  // Obtains the feature at a given position.
  rpc GetFeature(Point) returns (Feature) {}
}

Esse é um método RPC unário: um RPC simples em que o cliente envia uma solicitação ao servidor e aguarda uma resposta, assim como uma chamada de função local.

4. Gerar o código do cliente e do servidor

Em seguida, gere o código gRPC boilerplate para o cliente e o servidor do arquivo .proto usando o compilador de buffer de protocolo.

Para a geração de código Python do gRPC, criamos o grpcio-tools. Ela inclui:

  1. O compilador protoc comum que gera código Python com base em definições message.
  2. Plug-in protobuf do gRPC que gera código Python (stubs de cliente e servidor) das definições service.

Vamos instalar o pacote Python grpcio-tools usando pip. Vamos criar um novo ambiente virtual do Python (venv) para isolar as dependências do projeto dos pacotes do sistema:

python3 -m venv --upgrade-deps .venv

Para ativar o ambiente virtual no shell bash/zsh:

source .venv/bin/activate

Para Windows e shells não padrão, consulte a tabela em https://docs.python.org/3/library/venv.html#how-venvs-work.

Em seguida, instale o grpcio-tools (isso também instala o pacote grpcio):

pip install grpcio-tools

Use o comando a seguir para gerar o código boilerplate do Python:

python -m grpc_tools.protoc --proto_path=./protos  \
 --python_out=. --pyi_out=. --grpc_python_out=. \
 ./protos/route_guide.proto

Isso vai gerar os seguintes arquivos para as interfaces definidas em route_guide.proto:

  1. route_guide_pb2.py contém o código que cria classes dinamicamente geradas das definições message.
  2. route_guide_pb2.pyi é um "arquivo stub" ou "arquivo de dica de tipo" gerado das definições message. Ele contém apenas as assinaturas, sem implementação. Os arquivos stub podem ser usados por IDEs para oferecer melhor preenchimento automático e detecção de erros.
  3. O route_guide_pb2_grpc.py é gerado com base nas definições service e contém classes e funções específicas do gRPC.

O código específico do gRPC contém:

  1. RouteGuideStub, que pode ser usado por um cliente gRPC para invocar RPCs do RouteGuide.
  2. RouteGuideServicer, que define a interface para implementações do serviço RouteGuide.
  3. Função add_RouteGuideServicer_to_server usada para registrar um RouteGuideServicer em um servidor gRPC.

5. Criar o serviço

Primeiro, vamos ver como criar um servidor RouteGuide. A criação e a execução de um servidor RouteGuide são divididas em dois itens de trabalho:

  • Implementar a interface do serviço gerada da nossa definição de serviço com funções que realizam o "trabalho" real do serviço.
  • Executar um servidor gRPC em uma porta específica para detectar solicitações de clientes e transmitir respostas.

O servidor RouteGuide inicial pode ser encontrado em start_here/route_guide_server.py.

Implementar o RouteGuide

route_guide_server.py tem uma classe RouteGuideServicer que é subclasse da classe gerada route_guide_pb2_grpc.RouteGuideServicer:

# RouteGuideServicer provides an implementation
# of the methods of the RouteGuide service.
class RouteGuideServicer(route_guide_pb2_grpc.RouteGuideServicer):

RouteGuideServicer implementa todos os métodos de serviço RouteGuide.

Vamos analisar uma implementação simples de RPC em detalhes. O método GetFeature recebe um Point do cliente e retorna as informações de recurso correspondentes do banco de dados em Feature.

def GetFeature(self, request, context):
    feature = get_feature(self.db, request)
    if feature is None:
        return route_guide_pb2.Feature(name="", location=request)
    else:
        return feature

O método recebe uma solicitação route_guide_pb2.Point para a RPC e um objeto grpc.ServicerContext que fornece informações específicas da RPC, como limites de tempo limite. Ele retorna uma resposta route_guide_pb2.Feature.

Como iniciar o servidor

Depois de implementar todos os métodos RouteGuide, a próxima etapa é iniciar um servidor gRPC para que os clientes possam usar seu serviço:

def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    route_guide_pb2_grpc.add_RouteGuideServicer_to_server(
        RouteGuideServicer(),
        server,
    )
    listen_addr = "localhost:50051"
    server.add_insecure_port(listen_addr)
    print(f"Starting server on {listen_addr}")
    server.start()
    server.wait_for_termination()

O método do servidor start() não é bloqueador. Um novo thread será instanciado para processar solicitações. A linha de execução que chama server.start() geralmente não tem outro trabalho a fazer nesse meio tempo. Nesse caso, chame server.wait_for_termination() para bloquear corretamente a linha de execução da chamada até que o servidor seja encerrado.

6. Criar o cliente

Nesta seção, vamos criar um cliente para nosso serviço RouteGuide. É possível conferir o código do cliente inicial em start_here/route_guide_client.py.

Criar um stub

Para chamar métodos de serviço, primeiro precisamos criar um stub.

Vamos instanciar a classe RouteGuideStub do módulo route_guide_pb2_grpc, gerada do nosso .proto dentro do arquivo route_guide_client.py.

channel = grpc.insecure_channel("localhost:50051")
stub = route_guide_pb2_grpc.RouteGuideStub(channel)

Como chamar métodos de serviço

Para métodos RPC que retornam uma única resposta, conhecidos como métodos response-unary, o gRPC Python é compatível com semântica de fluxo de controle síncrona (bloqueio) e assíncrona (não bloqueio).

RPC simples

Primeiro, vamos definir um Point para chamar o serviço. Isso é tão simples quanto instanciar um objeto do pacote route_guide_pb2 com algumas propriedades:

point = route_guide_pb2.Point(latitude=412346009, longitude=-744026814)

Uma chamada síncrona para o RPC simples GetFeature é quase tão direta quanto chamar um método local. A chamada de RPC aguarda a resposta do servidor e retorna uma resposta ou gera uma exceção. Podemos chamar o método e ver a resposta assim:

feature = stub.GetFeature(point)
print(feature)

É possível inspecionar os campos do objeto "Feature" e gerar o resultado da solicitação:

if feature.name:
    print(f"Feature called '{feature.name}' at {format_point(feature.location)}")
else:
    print(f"Found no feature at at {format_point(feature.location)}")

7. Faça um teste

Execute o servidor:

python route_guide_server.py

Em outro terminal, ative o ambiente virtual novamente e execute o cliente:

python route_guide_client.py

Você vai ver uma saída como esta, com carimbos de data/hora omitidos para maior clareza:

name: "16 Old Brook Lane, Warwick, NY 10990, USA"
location {
  latitude: 412346009
  longitude: -744026814
}

Feature called '16 Old Brook Lane, Warwick, NY 10990, USA' at latitude: 412346009, longitude: -744026814

8. A seguir