Laboratório 1 - Introdução gRPC
Nesse laboratório vamos explorar a utilização do Framework gRPC para chamada de procedimento remoto. Além disso, também vamos utilizar protobuffer
para criação de menssagens e serviços que serão utilizados pelo gRPC. Ao final, vamos explorar um cenário de treinamento de modelos utilizando uma arquitetura cliente servidor
O que é gRPC?
gRPC ou Google Remote Procedure Call é uma estrutura RPC moderna de alto desempenho e código aberto que pode ser executada em qualquer ambiente. Ele pode conectar serviços de forma eficiente dentro e entre data centers com suporte plugável para balanceamento de carga, rastreamento, verificação de integridade e autenticação

RPC ou chamadas de procedimento remoto são as mensagens que o servidor envia ao sistema remoto para executar a tarefa (ou subrotinas). O gRPC foi projetado para facilitar a comunicação suave e eficiente entre os serviços. Ele pode ser utilizado de diferentes maneiras, como: * Conectando serviços poliglotas de forma eficiente em arquitetura de estilo de microsserviços * Conectando dispositivos móveis, clientes de navegador a serviços de backend * Gerando bibliotecas de cliente eficientes
Para o exemplo deste laboratório, utilizaremos Python para desenvolvimento. Dessa forma, para usar o grpc
devemos instalar os seguintes pacotes
pip install grpcio grpcio-tools sklearn
Note
O módulo sklearn
será utilizado no exercício descrito no final do laboratório.
Utilizando Proto Buffer
Protocol Buffer
é mecanismo eficiente e automatizado para serializar dados estruturados. Eles fornecem uma maneira de definir a estrutura dos dados a serem transmitidos (i.e., mensagens). O são melhores que XML, pois:
- Mais simples
- Até dez vezes menor
- Até 100 vezes mais rápido
- gera classes de acesso a dados que facilitam seu uso
Os protobuf são definidos em aquivos .proto
, os quais são bastante simples de usar. Nesse arquivos, definimos as mensagems que serão trocadas entre as RPCs e também os serviços, que definem as mensagem de entrada e também mensagens de resposta. Após definir a estrutura do arquivo .proto
podemos utilizar o compilador protoc
para gerar os arquivos de stubs e também métodos de codificação e decodificação que serão utilizados pelo programa de forma automática para linguagem de programação desejada.
Para o exemplo do PingPong
vamos definir o arquivo ping_pong.proto
com o seguinte conteúdo:
syntax = "proto3";
package pingponggprc;
message Ping {
string mensagem = 1;
}
message Pong {
string mensagem = 1;
double tempo = 2;
}
service PingPong{
rpc GetServerResponse(Ping) returns (Pong) {}
}
Compilando arquivo proto
Após definir o arquivo .proto
devemos compilá-lo utilizando o compilador de gRPC protoc
. Esse compilador está presente no módulo grpc_tools
e pode ser gerado utilizando o seguinte comando
python -m grpc_tools.protoc -I=. --python_out=. --grpc_python_out=. ping_pong.proto
Note
como resultado o compilador irá gerar os códigos para o apêndice do servidor (i.e., server stub ) e também para o apêndice do cliente (i.e., client stub ) os quais são responsáveis por enviar e receber as mensagens entre as chamadas RPC e também codificar e decodificar os dados que serão transmitidos.
Implementado Código Servidor
Agora precisamos implementar o código do servidor. Para esse código, devemos implementar uma classe que implemente o método GetServerResponse
que será responsável pela chamada definida no arquivo .proto
, esse método recebe uma requisição (i.e., a mensagem enviada pelo cliente) e também um contexto que oferece funcionalidade para a conexão com o cliente. Além disso, devemos implementar o método serve()
que ficará responsável por disponibilizar o serviço. Portanto, implementamos o seguinte código em um arquivo servidor.py
:
import grpc
from concurrent import futures
import time
import ping_pong_pb2_grpc as pb2_grpc
import ping_pong_pb2 as pb2
class ExemploServer(pb2_grpc.PingPongServicer):
def GetServerResponse(self, request, context):
mensagem = request.mensagem
resposta = f"Pong!"
mensagem_resposta = {
'mensagem' : resposta,
'tempo' : time.time()
}
return pb2.Pong(**mensagem_resposta)
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
pb2_grpc.add_PingPongServicer_to_server(ExemploServer(), server)
server.add_insecure_port('[::]:50051')
server.start()
print("Server started at 50051")
server.wait_for_termination()
if __name__ == '__main__':
serve()
Implementando Código Cliente
No cliente devemos implementar uma classe que se conecta no servidor e também invoca o método descrito no serviço passando a mensagem de entrada e recebendo uma mensagem de resposta. Dessa forma, para implementação do ping pong implementamos o seguinte código para o cliente em um arquivo cliente.py
:
import grpc
import ping_pong_pb2 as pb2
import ping_pong_pb2_grpc as pb2_grpc
import time
class ExemploGRPC(object):
def __init__(self):
self.host = 'localhost'
self.server_port = 50051
self.channel = grpc.insecure_channel(f"{self.host}:{self.server_port}")
self.stub = pb2_grpc.PingPongStub(self.channel)
def get_ping_response(self, message):
message = pb2.Ping(mensagem=message)
return self.stub.GetServerResponse(message)
if __name__ == '__main__':
client = ExemploGRPC()
mensagem = 'Ping!'
while True:
tempo = time.time()
print(f'Cliente -> {mensagem} {tempo}')
resposta = client.get_ping_response(mensagem)
print(f'Servidor -> {resposta.mensagem} {resposta.tempo}')
print(f'Duração Ping -> Pong: {time.time() - tempo}')
print('--------------------------------')
time.sleep(1)
Ping Pong
Por fim, para realizar o ping pong entre cliente servidor basta executar os respectivos códigos
Example
python servidor.py
python cliente.py
Exercício - Treinamento Modelos baseado em Cliente Servidor
Agora está na hora de colocar em prática o que foi abordado nesse laboratório. A idéia é realizar o treinamento de um modelo de aprendizado de máquina supervisionado para classificação (i.e, KNN, SVM, Decision Tree, Random Forest, etc). Os dados utilizados serão do dataset iris
presente no sklearn
, que pode ser importado utilizando
sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
iris = load_iris()
atributos = iris.data
rotulos = iris.target
x_treino, x_teste, y_treino, y_test = train_test_split(atributos, rotulos, test_size=0.2)
x
corresponde aos atributos e y
corresponde aos rótulos utilizaremos o train_test_split
do sklearn
. Mais detalhes sobre preparação dos dados para treinamento e avaliação serão apresentados nas próximas aulas.

Nesse exercício os dados estão disponíveis no cliente e o modelo está implementado no servidor, assim como apresentado na figura. Nesse cenário, o cliente deve encaminhar os uma mensagem com os dados para o servidor que deve treinar o modelo e retornar a acurácia de treinamento para os dados enviados via RPC.
Info
O código para treinamento do modelo no servidor será explicado detalhadamente na aula de revisão sobre aprendizado supervisionado e também na aula de treinamento de modelos.
Dessa forma, as seguintes tarefas devem ser realizadas:
- Gerar arquivo
.proto
com as mensagens que serão utilizadas pelo serviço para treinamento e avaliação de modelo. Por exemplo,FitRequest
,FitResponse
,PredictRequest
,PredictResponse
- Compilar o arquivo
.proto
para gerar os arquivos de stubs do cliente e servidor - Implementar o código do cliente que deve solicitar o treinamento do modelo e também a predição para outros dados após o treinamento
- Implementar o código do servidor que terá dois serviços, um para treinamento e outro para predição. O serviço de treinamento deve treinar o modelo e retornar a acurácia de treinamento, enquanto o serviço de predição deve receber uma amostra de dados e retornar a classificação para tal amostra
Tip
Para definir as mensagens que irá conter o dataset que será enviado para o servidor (i.e., array), você pode criar uma mensagem que represente uma amosta com dois campos, um utilizando repeated float
para representar os atributos do dataset e outra int32
para representar o rótulo. Em seguida, você pode criar uma outra mensagem que é um conjunto de amostras usando repeated nome_da_mensagem
Você pode utilizar qualquer classificador disponível no sklearn
, como por exemplo
from sklearn.neighbors import KNeighborsClassifier
model = KNeighborsClassifier()
.append()
Para o treinamento do modelo no servidor você pode utilizar o seguinte código
#Treina o modelo para os dados entradas e labels recebidos
model.fit(input_data, input_labels)
#calcula a acurácia do modelo para os dados de treino.
accuracy = model.score(input_data, input_labels)
model.predict(entrada)
o qual retorna a predição (i.e., classe) para a entrada fornecida