Comunicação Distribuída em Python com XML-RPC

O RPC (Remote Procedure Call) é um protocloo que permite a um programa executar procedimentos em outro espaço de endereçamento (geralmente em outro computador na rede) sem que o programador precise codificar explicitamente os detalehs da interação remota. O XML-RPC é uma implementação específica que utiliza HTTP como transporte e XML para codificação de dados, permitindo a comunicação entre sistemas heterogêneos de forma simplificada.

  1. Disponibilizando Funções com register_function

Para expor uma função específica no servidor, utilizamos o método register_function. O primeiro argumento é a função local e o segundo (opcional) é o nome pelo qual ela será chamada remotamente.

from xmlrpc.server import SimpleXMLRPCServer

# Configuração do servidor no host local na porta 8000
servidor = SimpleXMLRPCServer(("localhost", 8000))

def calcular_dobro(numero):
    """Retorna o dobro do valor recebido"""
    return numero * 2

# Registra a função para acesso remoto
servidor.register_function(calcular_dobro, "dobro")

print("Servidor de funções iniciado...")
servidor.serve_forever()

No lado do cliente, acessamos o serviço através de um ServerProxy:

import xmlrpc.client

# Conexão com o servidor
proxy = xmlrpc.client.ServerProxy("http://localhost:8000")

# Chamada remota da função
resultado = proxy.dobro(15)
print(f"O resultado do processamento remoto é: {resultado}")

  1. Exportando Objetos com register_instance

Em vez de registrar funções individualmente, é possível registrar uma instância de uma classe inteira. Todos os métodos públicos dessa instância ficarão disponíveis para o cliente.

from xmlrpc.server import SimpleXMLRPCServer

class OperacoesMatematicas:
    def multiplicar(self, a, b):
        return a * b

    def subtrair(self, a, b):
        return a - b

servidor = SimpleXMLRPCServer(("localhost", 8000))
# Registra todos os métodos da classe OperacoesMatematicas
servidor.register_instance(OperacoesMatematicas())

print("Servidor de instâncias rodando...")
servidor.serve_forever()

O cliente interage com os métodos da classe normalmente:

import xmlrpc.client

proxy = xmlrpc.client.ServerProxy("http://localhost:8000")
print(f"Multiplicação: {proxy.multiplicar(10, 5)}")
print(f"Subtração: {proxy.subtrair(20, 8)}")

  1. Customização com o Método _dispatch

O método especial _dispatch permite interceptar todas as chamadas remotas antes de serem executadas. Isso é útil para auditoria, controle de acesso ou roteamento personalizado de funções.

from xmlrpc.server import SimpleXMLRPCServer

class GerenciadorRemoto:
    def _dispatch(self, metodo, params):
        print(f"Log: Tentativa de execução do método '{metodo}' com os parâmetros {params}")
        func = getattr(self, metodo)
        return func(*params)

    def saudacao(self, nome):
        return f"Olá, {nome}! Bem-vindo ao sistema distribuído."

servidor = SimpleXMLRPCServer(("localhost", 8000))
servidor.register_instance(GerenciadorRemoto())
servidor.serve_forever()

  1. Implementação de Servidor Multi-thread

Por padrão, o SimpleXMLRPCServer processa uma requisição por vez. Para suportar múltiplos clientes simultaneamente, devemos herdar de ThreadingMixIn e SimpleXMLRPCServer.

from xmlrpc.server import SimpleXMLRPCServer
from socketserver import ThreadingMixIn

# Classe para suporte a múltiplas conexões concorrentes
class ServidorConcorrente(ThreadingMixIn, SimpleXMLRPCServer):
    pass

def concatenar_strings(prefixo, sufixo):
    return f"{prefixo} {sufixo}"

if __name__ == "__main__":
    servidor = ServidorConcorrente(("localhost", 9000))
    servidor.register_function(concatenar_strings, "combinar")
    print("Servidor multi-thread pronto para conexões...")
    servidor.serve_forever()

  1. Transferência de Arquivos e Dados Binários

Para transmitir arquivos ou dados binários via XML-RPC, utilizamos o wrapper xmlrpc.client.Binary. Isso garante que os dados não sejam corrompidos pela codificação XML.

Lado do Servidor

É importante habilitar allow_none=True se suas funções retornarem None.

from xmlrpc.server import SimpleXMLRPCServer
import xmlrpc.client

def salvar_upload(dados_binarios, nome_arquivo):
    with open(f"servidor_{nome_arquivo}", "wb") as f:
        f.write(dados_binarios.data)
    return True

def preparar_download(nome_arquivo):
    with open(nome_arquivo, "rb") as f:
        return xmlrpc.client.Binary(f.read())

servidor = SimpleXMLRPCServer(("localhost", 8000), allow_none=True)
servidor.register_function(salvar_upload, "upload")
servidor.register_function(preparar_download, "download")
servidor.serve_forever()

Lado do Cliente

import xmlrpc.client

proxy = xmlrpc.client.ServerProxy("http://localhost:8000")

# Enviando um arquivo
with open("imagem_local.png", "rb") as f:
    proxy.upload(xmlrpc.client.Binary(f.read()), "imagem_local.png")

# Baixando um arquivo
dados_remotos = proxy.download("arquivo_no_servidor.pdf")
with open("arquivo_baixado.pdf", "wb") as f:
    f.write(dados_remotos.data)

Tags: Python XML-RPC Distributed Systems backend Network Programming

Publicado em 7-24 10:54