Python XML-RPC

XML-RPC é um protocolo RPC (Remote Procedure Call) codificado em XML, é talvez o protocolo RPC mais simples face a outras implementações como RMI, CORBA, SOAP, etc…

Basicamente existe um servidor e um ou mais clientes que trocam mensagens em XML entre si.
O cliente pede ao servidor que execute determinado procedimento através de uma mensagem XML, o servidor executa o procedimento pedido e retorna o resultado também através de uma mensagem XML.

Desta forma é possível ao cliente fazer uso das funcionalidades do servidor sem ter de se preocupar com a implementação dos procedimentos. Uma grade vantagem desta implementação é a de permitir a integração entre linguagens, por exemplo podemos ter o lado do servidor implementados em Java e ter um cliente em PHP ou Python e vice-versa. Como é óbvio é possível que se encontrem algumas contradições entre as linguagens em especial nas estruturas de dados, mas como as mensagens são em XML ou seja texto é sempre possível contornar os pequenos obstáculos que possam aparecer.

Python vem de core com suporte para este protocolo de comunicação, para isso tem o modulo xmlrpclib para o interface de cliente e o modulo SimpleXMLRCPServer para o interface de servidor.

Para exemplificar algumas das funcionalidades destes módulos vamos implementar um pequeno servidor e cliente ambos em Python recorrendo ao uso dos referidos módulos.

# -*- coding: UTF-8 -*-
from SimpleXMLRPCServer import SimpleXMLRPCServer
from time import strftime

class User (object):

	def __init__(self, username, password):
		self.__username = username
		self.__password = password
		self.__logged = False

	def getUsername(self):
		return self.__username

	def getPassword(self):
		return self.__password

	def session(self, state=None):
		if state != None:
			self.__logged = state
		return self.__logged

class Session (object):
	"""
	Fornece os funcionalidades básicas para um sistema
	de controlo de sessão.
	"""

	def __init__(self):
		"""
		Inicia um dicionário vazio que servirá com
		base de dados.
		"""
		self.__db = {}

	def login(self,username, password):
		"""
		Altera o estado da sessão para True.
		Args: username, password
		Return: True ou False
		"""
		user = self.__db[username]
		if user.getPassword == password:
			user.session(True)
		return user.session(True)

	def register(self,username, password):
		"""
		Insere um novo user na base de dados.
		Args: username, password
		Return: True ou False
		"""
		self.__db[username] = User(username, password)
		return self.__db.has_key(username)

	def logout(self,username):
		"""
		Altera o estado da sessão para False.
		Args: username
		Return: True ou False
		"""
		user = self.__db[username]
		return not user.session(False)

def currentTime():
	"""
	Permite obter a data e hora actual do servidor.
	Args: None
	Return: String com Data e hora
	Formato da Resposta: YYYY-mm-DD HH:MM:SS
	"""
	return strftime("%Y-%m-%d %H:%M:%S")

def start(addr="localhost",port=8080):
	#Cria um servidor XML-RPC no endereço e port definido.
	server = SimpleXMLRPCServer((addr,port))
	#Permite aos clientes fazer introspecção ao servidor
	server.register_introspection_functions()
	#Permite aos clientes fazerem vários pedidos como um só
	server.register_multicall_functions()
	#Regista um objecto Session, o mapeamento dos métodos é automático
	server.register_instance(Session())
	#Regista a funcão currentTime no servidor com o nome time
	server.register_function(currentTime,"time")
	#Inicia o servidor XML-RCP em loop infinito
	server.serve_forever()

if __name__ == "__main__":
	start()

[Servidor]

Como se pode ver é um exemplo muito muito simples e não totalmente realista, mas para o que se pretende serve perfeitamente. Basicamente temos o servidor que permite registar, fazer login e logout de utilizadores bem como saber a data/hora actual do servidor.

Os procedimentos podem ser disponibilizados através de funções isoladas ou através de instâncias de classe, ao registar um instância todos os métodos públicos são automaticamente publicados para os cliente XML-RPC.

Passamos agora ao cliente, o cliente vai ter duas formas de fazer os pedidos ao servidor, o método directo em que cada pedido é feito com uma mensagem e o método multicall que faz os pedidos todos de uma só vez e recebe as respostas também todas juntas.

# -*- coding: UTF-8 -*-
import xmlrpclib
from datetime import datetime

#Cria uma ligação ao servidor XML-RCP
server = xmlrpclib.ServerProxy("http://localhost:8080/")

def pedidoSimples():
	#Pede ao servidor que execute o procedimento time
	print server.time()
	#Pede ao servidor que liste todos os procedimentos disponíveis
	print server.system.listMethods()
	#Retorna o pydoc associado ao método
	print server.system.methodHelp("time")
	#Envia os dados para o método junto com o pedido
	print server.register("magician","123")
	print server.login("magician","123")
	print server.logout("magician")

def pedidoMultiCall():
	#Gera um pedido MultiCall
	multirequest = xmlrpclib.MultiCall(server)
	#Adiciona ao pedido multi call os pedidos pretendidos
	multirequest.time()
	multirequest.register("magician","123")
	multirequest.login("magician","123")
	#Faz o pedido e aguarda a resposta que é um gerador
	result = multirequest()
	#Um a um extrai os dados da resposta do gerador
	for resp in result:
		print resp

if __name__ == "__main__":
	#Envia uma sequência de pedidos um a um
	pedidoSimples()
	#Envia uma sequência de pedidos como um só
	pedidoMultiCall()

[Cliente]

2010-07-23 16:55:49
['login', 'logout', 'register', 'system.listMethods', 'system.methodHelp', 'system.methodSignature', 'system.multicall', 'time']
Permite obter a data e hora actual do servidor.
Args: None
Return: String com Data e hora
Formato da Resposta: YYYY-mm-DD HH:MM:SS
True
True
True
2010-07-23 16:55:49
True
True

[Output]

Como se pode ver o processo é muito simples tanto o método de pedido directo como o de multicall são bastante intuitivos, a vantagem do método multicall é que ao invés de fazer vários pedidos e receber varias respostas faz apenas um pedido e recebe apenas uma resposta o que pode ajudar bastante no que toca a performance principalmente no congestionamento do servidor.

Outra coisa importante sobre o MultiCall é que cada objecto deste classe embora seja reutilizável os pedidos já lá registados não são removidos mesmo depois de ser executado. Ou seja neste exemplo se após o loop para imprimir os resultados se fosse adicionado outro pedido e se volta-se a ser executado ele iria enviar novamente todos os pedidos anteriores mais o novo pedido. Por isso caso se pretenda fazer novos pedidos em bloco (multicall) deve-se criar um novo objecto MultiCall.

Basicamente é isto, existem mais alguns pormenores em relação aos modulos mas nada que um leitura pela documentação e uma vista de olhos pelos exemplos lá colocados não clarifique.

Referencias:

Python SimpleXMLRPCServer

Python xmlrpclib

XML-RPC Spec

XML-RPC Instrospection

XML-RPC Errata

XML-RPC HOW-TO

Anúncios

Deixe uma Resposta

Preencha os seus detalhes abaixo ou clique num ícone para iniciar sessão:

Logótipo da WordPress.com

Está a comentar usando a sua conta WordPress.com Terminar Sessão /  Alterar )

Google photo

Está a comentar usando a sua conta Google Terminar Sessão /  Alterar )

Imagem do Twitter

Está a comentar usando a sua conta Twitter Terminar Sessão /  Alterar )

Facebook photo

Está a comentar usando a sua conta Facebook Terminar Sessão /  Alterar )

Connecting to %s