Logo UFPR Logo Licenciatura

RPC - Sistemas Distribuídos

O Conceito de RPC

RPC (Remote Procedure Call) é um paradigma de comunicação entre processos que permite que um programa invoque procedimentos ou funções em outra máquina remota como se fossem locais, abstraindo a complexidade da comunicação em rede.

Analogia: Imagine fazer um pedido em um restaurante - você (cliente) chama o garçom (stub cliente), que leva seu pedido (requisição) para a cozinha (servidor). O chef (procedimento remoto) prepara o prato (processa a requisição) e o garçom traz a resposta (resultado) para você, sem que você precise saber como a comida foi preparada.

Características Principais

Histórico

O conceito de RPC foi introduzido nos anos 1980 por Birrell e Nelson como parte do sistema Cedar na Xerox PARC. Tornou-se popular com a implementação do Sun RPC (agora conhecido como ONC RPC), que ainda é usado em sistemas NFS.

Funcionamento Detalhado do RPC

Fluxo Básico

  1. Chamada do cliente: O programa cliente chama uma função como se fosse local
  2. Serialização (marshalling): O stub cliente converte os parâmetros em um formato de rede
  3. Transmissão: A requisição é enviada via protocolo de transporte (TCP/UDP)
  4. Deserialização (unmarshalling): O stub servidor converte os dados para o formato local
  5. Execução: O procedimento real é executado no servidor
  6. Retorno: O resultado segue o caminho inverso até o cliente

Componentes Arquiteturais

Exemplo de Código

Interface (IDL - Interface Definition Language):

interface Calculadora {
    double soma(in double a, in double b);
}

Cliente (pseudocódigo):

resultado = calculadora_remota.soma(5.2, 3.8);
print(resultado); // 9.0

Servidor (pseudocódigo):

implementação soma(double a, double b) {
    return a + b;
}

Considerações de Projeto

Exemplos Práticos de RPC

1. Sistema Bancário (gRPC)

Conceitos-chave: Definição de interfaces, Serialização binária, Chamadas síncronas

Definição do Serviço (Protocol Buffers)
service BankService {
    rpc GetBalance(AccountRequest) returns (BalanceResponse);
    rpc Transfer(TransferRequest) returns (TransferResponse);
}

Aqui definimos o contrato do serviço usando Protocol Buffers:

  • service declara um conjunto de operações remotas
  • rpc define cada procedimento remoto com seus tipos de mensagem
  • As mensagens (AccountRequest/BalanceResponse) serão definidas separadamente
Implementação do Servidor (Python)
class BankServicer(bank_pb2_grpc.BankServiceServicer):
    def GetBalance(self, request, context):
        # 1. Extrai o ID da conta da requisição
        account_id = request.account_id
        
        # 2. Consulta o banco de dados (simulado)
        balance = database.get_balance(account_id)
        
        # 3. Constrói e retorna a resposta
        return bank_pb2.BalanceResponse(
            balance=balance.amount,
            currency=balance.currency
        )

No servidor:

  1. Herda-se da classe gerada pelo compilador gRPC
  2. Cada método RPC recebe:
    • request: dados desserializados
    • context: metadados da chamada
  3. A lógica de negócios é executada como em uma função local
  4. O retorno é automaticamente serializado para envio ao cliente

2. Autenticação (JSON-RPC)

Conceitos-chave: RPC baseado em texto, Formato independente de linguagem, Semântica requisição-resposta

Requisição do Cliente
{
    "jsonrpc": "2.0",
    "method": "AuthService.Authenticate",
    "params": {
        "username": "joao",
        "password": "senha123"
    },
    "id": 1
}

Estrutura da requisição JSON-RPC:

Campo Finalidade
jsonrpc Versão do protocolo (deve ser "2.0")
method Nome do serviço e método a ser chamado
params Objeto com os parâmetros da chamada
id Identificador único para correlacionar respostas
Resposta do Servidor
{
    "jsonrpc": "2.0",
    "result": {
        "authenticated": true,
        "user_id": 1023,
        "access_token": "eyJhbGciOi..."
    },
    "id": 1
}

Resposta bem-sucedida contém:

  • result: dados retornados pelo método
  • id: mesmo ID da requisição correspondente

Em caso de erro, haveria um campo error em vez de result com detalhes do problema.

3. Sistema de Recomendações (Apache Thrift)

Conceitos-chave: Definição de interfaces com IDL, Tipos complexos, Tratamento de erros remotos

Definição da Interface (Thrift IDL)
service RecommendationService {
    list<Product> GetRecommendations(
        1: i32 user_id,
        2: i16 num_items,
        3: map<string, string> context
    ) throws (1: RecommendationError error)
}

Características importantes:

  1. Tipos de parâmetros são explicitamente definidos (i32, i16)
  2. Suporte a coleções complexas (list, map)
  3. Declaração explícita de erros que podem ser lançados (throws)
  4. Números de campo (1:, 2:) permitem evolução da API

4. Sistema de Arquivos Distribuídos (ONC RPC)

Conceitos-chave: RPC clássico, Serialização XDR, Operações remotas em sistemas de arquivos

Chamada em C
readargs args = {
    .file = file_handle,
    .offset = 0,
    .count = 1024
};

readres *result = nfsproc_read_1(&args, cl);
if (result != NULL) {
    fwrite(result->filedata, 1, result->count, stdout);
}

Fluxo da operação:

1. Prepara argumentos
2. Chama nfsproc_read_1 (stub gerado)
3. Stub serializa os dados (XDR)
4. Envia pela rede
5. Espera resposta
6. Desserializa resultado
7. Retorna ponteiro para estrutura

Ferramentas Populares para RPC

Por que usar frameworks RPC? Eles abstraem a complexidade da comunicação de rede, oferecendo serialização eficiente, descoberta de serviços e tratamento de erros.

gRPC

Criado por: Google

Linguagens: Multiplataforma (C++, Java, Python, Go, etc)

Destaque: Usa Protocol Buffers (binário) e HTTP/2

// Exemplo de definição
service UserService {
  rpc GetUser (UserRequest) returns (UserResponse);
}

Melhor para: Microserviços de alta performance e streaming bidirecional

Java RMI

Criado por: Sun Microsystems

Linguagens: Apenas Java

Destaque: Integração nativa com JVM

// Interface remota
public interface Calculator extends Remote {
  int add(int a, int b) throws RemoteException;
}

Melhor para: Aplicações corporativas Java puras

Apache Thrift

Criado por: Facebook

Linguagens: Multiplataforma

Destaque: Suporte a tipos complexos e herança

// Exemplo Thrift IDL
service Calculator {
  i32 add(1:i32 num1, 2:i32 num2)
}

Melhor para: Sistemas com múltiplas linguagens interoperando

XML-RPC / JSON-RPC

Padrão: Aberto/SOAP

Linguagens: Qualquer com parser XML/JSON

Destaque: Legibilidade humana

// Exemplo JSON-RPC
{
  "jsonrpc": "2.0",
  "method": "subtract",
  "params": [42, 23],
  "id": 1
}

Melhor para: Integrações web simples e APIs públicas

Pyro5 (Python)

Criado por: Comunidade Python

Linguagens: Python

Destaque: Simplicidade e Python puro

# Exemplo Pyro5
import Pyro5.api

@Pyro5.api.expose
class Calculadora:
    def soma(self, a, b):
        return a + b

Melhor para: Prototipagem rápida e sistemas Python homogêneos

Comparativo Técnico

Ferramenta Serialização Transporte Overhead Complexidade
gRPC Protocol Buffers (binário) HTTP/2 Baixo Média
Java RMI Serialização Java JRMP Alto Alta
Apache Thrift Binary/JSON/... TCP/HTTP Médio Média
JSON-RPC JSON (texto) HTTP Alto Baixa
Pyro5 Serpent (Python) TCP Médio Baixa

Exemplo: RPC com Python e XML-RPC

XML-RPC é um protocolo RPC que usa XML para codificação e HTTP como transporte. Ideal para integrações simples entre sistemas heterogêneos.

Arquitetura do Exemplo

1. Cliente chama proxy.square(5)
2. XML-RPC serializa para: <methodCall><methodName>square</methodName><params><param><value><i4>5</i4></value></param></params></methodCall>
3. Servidor recebe, desserializa e executa
4. Retorna: <methodResponse><params><param><value><i4>25</i4></value></param></params></methodResponse>
5. Cliente desserializa e mostra resultado

Servidor XML-RPC

from xmlrpc.server import SimpleXMLRPCServer

class MathOperations:
    def square(self, num):
        """Calcula o quadrado de um número"""
        return num * num
    
    def cube(self, num):
        """Calcula o cubo de um número"""
        return num ** 3

# 1. Cria instância do servidor na porta 8000
server = SimpleXMLRPCServer(("localhost", 8000), allow_none=True)

# 2. Registra funções remotas
server.register_function(MathOperations().square, "square")
server.register_function(MathOperations().cube, "cube")

# 3. Ativa introspecção (lista métodos disponíveis)
server.register_introspection_functions()

print("Servidor XML-RPC rodando em http://localhost:8000")
try:
    # 4. Mantém servidor ativo
    server.serve_forever()
except KeyboardInterrupt:
    print("\nEncerrando servidor...")

Funcionamento Detalhado:

  1. SimpleXMLRPCServer: Classe que implementa o servidor RPC básico
  2. Registro de métodos: Cada função deve ser explicitamente registrada
  3. Introspecção: Permite clientes descobrirem métodos disponíveis
  4. Segurança: Por padrão só aceita conexões locais (altere o endereço para '0.0.0.0' para rede)

Cliente XML-RPC

import xmlrpc.client

# 1. Cria proxy para conexão com servidor
proxy = xmlrpc.client.ServerProxy("http://localhost:8000")

# 2. Chama métodos remotos como se fossem locais
try:
    number = 5
    print(f"Quadrado de {number}: {proxy.square(number)}")
    print(f"Cubo de {number}: {proxy.cube(number)}")
    
    # 3. Lista métodos disponíveis (se introspecção ativada)
    print("\nMétodos disponíveis:")
    print(proxy.system.listMethods())
    
except ConnectionError as e:
    print(f"Erro de conexão: {e}")
except Exception as e:
    print(f"Erro durante chamada RPC: {e}")

Características do Cliente:

  • ServerProxy: Representação local do servidor remoto
  • Transparência: Chamadas remotas parecem locais
  • Tratamento de erros: Importante para lidar com falhas de rede
  • Introspecção: system.listMethods() mostra operações disponíveis

Tópicos Avançados

1. Servidor Multithread

from xmlrpc.server import SimpleXMLRPCServer
from socketserver import ThreadingMixIn

class ThreadedXMLRPCServer(ThreadingMixIn, SimpleXMLRPCServer):
    pass

server = ThreadedXMLRPCServer(("localhost", 8000), allow_none=True)

Permite lidar com múltiplas requisições simultaneamente.

2. Chamadas Assíncronas

import xmlrpc.client
import threading

def async_call(proxy, method, args, callback):
    def worker():
        result = getattr(proxy, method)(*args)
        callback(result)
    threading.Thread(target=worker).start()

def print_result(result):
    print("Resultado:", result)

async_call(proxy, "square", (5,), print_result)

Implementa padrão callback para não bloquear a thread principal.

Notas de Segurança

⚠️ Atenção: O XML-RPC padrão não é seguro para uso em redes públicas!

  • Não use allow_none=True em produção
  • Para ambiente real, considere:
    • XML-RPC sobre HTTPS
    • Autenticação básica HTTP
    • Restrição de IPs clientes
    • Usar alternativas como JSON-RPC ou gRPC

Testando o Servidor XML-RPC com Postman

O Postman pode ser usado para testar chamadas XML-RPC manualmente, enviando requisições HTTP brutas com o payload XML.

Passo a Passo para Testar

1. Configurar a Requisição

Método: POST

URL:http://localhost:8000

Headers:

  • Content-Type: text/xml
  • User-Agent: Postman
Configuração Postman

2. Criar o Corpo da Requisição (XML)

<?xml version="1.0"?>
<methodCall>
    <methodName>square</methodName>
    <params>
        <param>
            <value><i4>8</i4></value>
        </param>
    </params>
</methodCall>

No Postman:

  1. Selecione a tab Body
  2. Escolha raw
  3. Selecione XML no dropdown
  4. Cole o XML acima

3. Enviar e Verificar a Resposta

Resposta esperada (sucesso):

<?xml version='1.0'?>
<methodResponse>
    <params>
        <param>
            <value><i4>64</i4></value>
        </param>
    </params>
</methodResponse>

Em caso de erro:

<?xml version='1.0'?>
<methodResponse>
    <fault>
        <value>
            <struct>
                <member>
                    <name>faultCode</name>
                    <value><i4>105</i4></value>
                </member>
                <member>
                    <name>faultString</name>
                    <value><string>Method not found</string></value>
                </member>
            </struct>
        </value>
    </fault>
</methodResponse>

Coleção Postman Pronta

Importe esta coleção pronta para testar rapidamente:

Coleção JSON para Importar
{
  "info": {
    "_postman_id": "a1b2c3d4-e5f6-7890",
    "name": "XML-RPC Test Collection",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "item": [
    {
      "name": "Square Method",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "text/xml"
          }
        ],
        "body": {
          "mode": "raw",
          "raw": "<?xml version=\"1.0\"?>\n<methodCall>\n    <methodName>square</methodName>\n    <params>\n        <param>\n            <value><i4>8</i4></value>\n        </param>\n    </params>\n</methodCall>",
          "options": {
            "raw": {
              "language": "xml"
            }
          }
        },
        "url": {
          "raw": "http://localhost:8000",
          "protocol": "http",
          "host": ["localhost"],
          "port": "8000"
        }
      }
    },
    {
      "name": "List Methods",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "text/xml"
          }
        ],
        "body": {
          "mode": "raw",
          "raw": "<?xml version=\"1.0\"?>\n<methodCall>\n    <methodName>system.listMethods</methodName>\n    <params></params>\n</methodCall>",
          "options": {
            "raw": {
              "language": "xml"
            }
          }
        },
        "url": {
          "raw": "http://localhost:8000",
          "protocol": "http",
          "host": ["localhost"],
          "port": "8000"
        }
      }
    }
  ]
}

Como Importar:

  1. No Postman, clique em Import
  2. Selecione a tab Raw Text
  3. Cole o JSON acima
  4. Clique em Import

A coleção inclui exemplos para:

  • Chamar o método square
  • Listar todos os métodos disponíveis

Dicas Avançadas de Teste

1. Teste de Carga Básica

Use o Postman Runner para enviar múltiplas requisições sequenciais:

  1. Crie um ambiente com variável {{number}}
  2. Na aba Tests, adicione:
    // Gera número aleatório entre 1-100
    pm.environment.set("number", Math.floor(Math.random() * 100) + 1);
  3. No corpo XML, use <i4>{{number}}</i4>
  4. Execute a coleção 10-20 vezes

2. Validação de Resposta

Adicione scripts de teste na aba Tests:

// Verifica se a resposta é XML válido
pm.test("Content-Type is XML", function() {
    pm.expect(pm.response.headers.get("Content-Type")).to.include("text/xml");
});

// Verifica se o status é 200
pm.test("Status code is 200", function() {
    pm.response.to.have.status(200);
});

// Parseia o XML e verifica o resultado
const response = xml2Json(pm.response.text());
pm.test("Result is valid", function() {
    pm.expect(response.methodResponse.params.param.value.i4).to.exist;
});

Exemplo Avançado: Sistema de Notificações com gRPC

gRPC é um framework RPC moderno que usa Protocol Buffers para serialização e HTTP/2 como transporte, ideal para sistemas em tempo real.

1. Definição do Serviço (arquivo .proto)

syntax = "proto3";

service NotificationService {
  // Streaming bidirecional para notificações em tempo real
  rpc Subscribe (SubscriptionRequest) returns (stream Notification);
  
  // Chamada unária tradicional
  rpc SendNotification (SendRequest) returns (SendResponse);
}

message SubscriptionRequest {
  string user_id = 1;  // ID do usuário assinante
}

message Notification {
  string from_user = 1;   // Remetente
  string message = 2;     // Conteúdo
  string timestamp = 3;   // Data/hora no formato ISO
}

message SendRequest {
  string from_user = 1;  // Quem envia
  string to_user = 2;    // Destinatário
  string message = 3;    // Conteúdo
}

message SendResponse {
  bool success = 1;      // Confirmação de entrega
}

Componentes Principais:

  • stream Notification: Indica fluxo contínuo de mensagens
  • message: Define estruturas de dados serializáveis
  • rpc: Declara os endpoints remotos

2. Implementação do Servidor (Python)

import time
import threading
from concurrent import futures

class NotificationService(notification_pb2_grpc.NotificationServiceServicer):
    def __init__(self):
        self.subscriptions = {}
        self.lock = threading.Lock()

    def Subscribe(self, request, context):
        """Implementação do streaming server-side"""
        user_id = request.user_id
        
        # Adiciona usuário à lista de assinantes
        with self.lock:
            if user_id not in self.subscriptions:
                self.subscriptions[user_id] = []
        
        # Mantém conexão ativa enquanto o cliente estiver conectado
        while context.is_active():
            with self.lock:
                if self.subscriptions[user_id]:
                    yield self.subscriptions[user_id].pop(0)
            
            # Pausa para evitar consumo excessivo de CPU
            time.sleep(1)

    def SendNotification(self, request, context):
        """Método unário tradicional"""
        with self.lock:
            if request.to_user in self.subscriptions:
                notification = notification_pb2.Notification(
                    from_user=request.from_user,
                    message=request.message,
                    timestamp=datetime.now().isoformat()
                )
                self.subscriptions[request.to_user].append(notification)
                return notification_pb2.SendResponse(success=True)
        return notification_pb2.SendResponse(success=False)

Pontos-chave:

  1. threading.Lock: Garante thread-safety nas operações
  2. context.is_active(): Verifica se conexão está válida
  3. yield: Envia mensagens conforme disponíveis (streaming)

3. Cliente Python

import grpc
import notification_pb2
import notification_pb2_grpc

def run_client():
    channel = grpc.insecure_channel('localhost:50051')
    stub = notification_pb2_grpc.NotificationServiceStub(channel)
    
    # 1. Inicia assinatura (streaming)
    user_id = "user_123"
    response_stream = stub.Subscribe(
        notification_pb2.SubscriptionRequest(user_id=user_id)
    
    # Thread para receber notificações
    def listen_notifications():
        try:
            for notification in response_stream:
                print(f"\nNova notificação de {notification.from_user}:")
                print(f"{notification.message}")
                print(f"Em {notification.timestamp}")
        except grpc.RpcError as e:
            print(f"Conexão falhou: {e.code()}")
    
    # 2. Envia notificação (unário)
    def send_message():
        response = stub.SendNotification(
            notification_pb2.SendRequest(
                from_user="user_456",
                to_user="user_123",
                message="Olá, como vai você?"
            )
        )
        print("Notificação enviada com sucesso!" if response.success else "Falha ao enviar")
    
    # Executa em threads separadas
    threading.Thread(target=listen_notifications, daemon=True).start()
    threading.Thread(target=send_message).start()

Fluxo do Cliente:

1. Cria canal gRPC
2. Inicia streaming com Subscribe()
3. Processa notificações em thread separada
4. Envia mensagens com SendNotification()

4. Recursos Avançados

Interceptores

class AuthInterceptor(grpc.ServerInterceptor):
    def intercept_service(self, continuation, handler_call_details):
        metadata = dict(handler_call_details.invocation_metadata)
        if not validate_token(metadata.get('authorization')):
            raise grpc.RpcError(grpc.StatusCode.UNAUTHENTICATED)
        return continuation(handler_call_details)

# Adiciona ao servidor:
server = grpc.server(
    futures.ThreadPoolExecutor(),
    interceptors=[AuthInterceptor()]
)

Adiciona autenticação JWT antes de processar chamadas.

Deadlines

# No cliente:
try:
    response = stub.SendNotification(
        request,
        timeout=10  # 10 segundos de timeout
    )
except grpc.RpcError as e:
    if e.code() == grpc.StatusCode.DEADLINE_EXCEEDED:
        print("Servidor não respondeu a tempo")

Evita chamadas bloqueantes indefinidamente.

Laboratório Prático: Sistema de Leilão com Pyro5

Este laboratório demonstra um sistema de leilão distribuído usando Pyro5, um framework RPC puro para Python. O sistema permite:

  • Criação de leilões com itens e tempo determinado
  • Lances concorrentes de múltiplos clientes
  • Consulta de resultados ao final do leilão

1. Classe Principal do Serviço

@Pyro5.api.expose
class AuctionService:
    def __init__(self):
        self.auctions = {}  # Armazena todos os leilões
        self.lock = Lock()  # Sincronização de threads

Elementos Chave:

  • @Pyro5.api.expose: Torna a classe acessível remotamente
  • self.auctions: Dicionário que mantém o estado dos leilões
  • self.lock: Garante segurança com múltiplos clientes

2. Criação de Leilão

def create_auction(self, item_name, starting_price, duration):
    with self.lock:
        auction_id = str(int(time.time()))  # ID único
        self.auctions[auction_id] = {
            'item': item_name,
            'current_price': starting_price,
            'end_time': time.time() + duration,
            'bids': []  # Histórico de lances
        }
        return auction_id

Fluxo:

  1. Recebe dados do item e duração
  2. Gera ID usando timestamp
  3. Armazena estrutura inicial do leilão
  4. Retorna ID para referência futura

Uso: create_auction("Relógio Vintage", 250.00, 3600) (1 hora)

3. Realização de Lances

def place_bid(self, auction_id, bidder_name, amount):
    with self.lock:
        auction = self.auctions.get(auction_id)
        if not auction:
            return False, "Leilão não encontrado"
        if time.time() > auction['end_time']:
            return False, "Leilão encerrado"
        if amount <= auction['current_price']:
            return False, "Lance muito baixo"
        
        auction['current_price'] = amount
        auction['bids'].append((bidder_name, amount))
        return True, "Lance aceito"

Validações:

1. Verifica existência do leilão
2. Confirma se está dentro do prazo
3. Valida se lance > valor atual
4. Atualiza estado do leilão

Retorno: Tupla (success: bool, message: str)

4. Consulta de Resultados

def get_auction_result(self, auction_id):
    with self.lock:
        auction = self.auctions.get(auction_id)
        if not auction:
            return None, "Leilão não encontrado"
        if time.time() < auction['end_time']:
            return None, "Leilão ainda em andamento"
        if not auction['bids']:
            return None, "Nenhum lance registrado"
        
        winner = max(auction['bids'], key=lambda x: x[1])
        return {
            'item': auction['item'],
            'winner': winner[0],
            'winning_bid': winner[1],
            'total_bids': len(auction['bids'])
        }, "Resultado obtido"

Processamento:

  • max(): Encontra o maior lance
  • Estrutura de retorno:
    {
        "item": str,
        "winner": str,
        "winning_bid": float,
        "total_bids": int
    }

Implementação Completa do Servidor

import Pyro5.api
import time
from threading import Lock

def start_server():
    daemon = Pyro5.api.Daemon()
    ns = Pyro5.api.locate_ns()
    uri = daemon.register(AuctionService)
    ns.register("auction.service", uri)
    
    print("Servidor de leilão pronto. URI:", uri)
    daemon.requestLoop()

if __name__ == "__main__":
    start_server()

Componentes Pyro5:

Elemento Função
Daemon() Recebe chamadas remotas
locate_ns() Conecta ao servidor de nomes
register() Disponibiliza o serviço

Atividade Prática

Exercício 1: Cliente Básico

Crie um cliente que:

  1. Conecta ao servidor de leilão
  2. Cria um novo leilão para um item fictício
  3. Realiza 3 lances sequenciais
  4. Verifica o resultado

Atividades Propostas

🔔

Notificações em Tempo Real

Estender o sistema de leilão com notificações push para participantes quando:

  • Novo lance é realizado
  • Leilão está prestes a encerrar
  • Resultado final é definido

Sugestão de implementação:

@Pyro5.api.expose
class AuctionCallback:
    def notify(self, message):
        print("Notificação:", message)

# Registrar callback no cliente
callback = AuctionCallback()
daemon.register(callback)
⏱️

Benchmark gRPC vs Pyro5

Comparar desempenho com:

  1. Medição de latência (tempo de resposta)
  2. Throughput (operações por segundo)
  3. Uso de recursos (CPU, memória)
Métrica gRPC Pyro5
Latência X ms Y ms
Throughput X ops/s Y ops/s
🔒

Sistema de Autenticação

Implementar:

  • Registro de usuários
  • Login com token JWT
  • Autorização por níveis de acesso
def place_bid(self, auction_id, amount, token):
    if not validate_jwt(token):
        raise PermissionError("Acesso negado")
    # Restante da lógica
💥

Testes de Resiliência

Simular e tratar:

  • Queda do servidor durante operações
  • Perda de conexão de rede
  • Timeout em chamadas remotas

Cenários para testar:

# Simular timeout
try:
    response = stub.call_method(request, timeout=5)
except grpc.RpcError as e:
    if e.code() == grpc.StatusCode.DEADLINE_EXCEEDED:
        # Lógica de retry
🌓