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.
- 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}")
- 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)}")
- 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()
- 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()
- 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)