2010-07-07 16 views
1

J'ai créé un serveur simple RPC pour effectuer certaines tâches communes à nos équipes, mais qui sont appelées à partir de différents réseaux. Le serveur ressemble à ceci (je ne comprends pas le traitement des erreurs par souci de concision):Ajout de méthodes à un serveur RPC simple de manière propre et séparée

from twisted.internet.protocol import Protocol, Factory 
from twisted.internet import reactor 
import json 

class MyProtocol(Protocol): 
    def dataReceived(self, data): 
     req = json.loads(data) # create a dictionary from JSON string 
     method = getattr(self, req['method']) # get the method 
     method(req['params']) # call the method 

    def add(self, params): 
     result = {} # initialize a dictionary to convert later to JSON 
     result['result'] = sum(params) 
     result['error'] = None 
     result['id'] = 1 
     self.transport.write(json.dumps(result)) # return a JSON string 
     self.transport.loseConnection() # close connection 

factory = Factory() 
factory.protocol = MyProtocol 
reactor.listenTCP(8080, factory) 
reactor.run() 

Ceci est très simple: le serveur reçoit une requête RPC JSON du client, recherche la méthode, et appelle le passage de la méthode Les paramètres. La méthode elle-même est celle qui renvoie la réponse JSON RPC. Pour les moins familiers, un JSON RPC ressemble à peu près comme ceci:

request: 
{"method":"my_method", "params":[1,2,3], "id":"my_id"} 
response: 
{"result":"my_result", "error":null, "id":"my_id"} 

Le serveur RPC comme je l'ai sert très bien mes actuels fins (comme vous pouvez l'imaginer, ma tâche est très simple). Mais je devrai continuer à ajouter des méthodes à mesure que la complexité de la tâche augmente. Je ne veux pas ouvrir le fichier principal et ajouter un autre def method3(...) et, deux semaines plus tard, ajouter def method4(...) et ainsi de suite; le code augmenterait trop rapidement et la maintenance serait de plus en plus difficile.

Alors, ma question est: comment puis-je créer une architecture qui me permet de registre méthodes dans le serveur. Un bonus serait d'avoir un dossier séparé contenant un fichier par méthode, afin qu'ils puissent facilement être partagés et maintenus. Cette "architecture" me permettrait également de reporter la maintenance de certaines méthodes à quelqu'un d'autre, quelle que soit leur compréhension de Twisted. Je ne m'inquiète pas si j'ai besoin de redémarrer le serveur chaque fois qu'une nouvelle méthode est enregistrée, mais un plus évident serait si je n'ai pas pas aussi :).

Merci.

Répondre

1

Un peu d'ordre largish;) mais voici quelques premières étapes pour vous (-UP moqué très fortement, les spécificités tordues ommited dans les exemples):

# your twisted imports... 
import json 

class MyProtocol(object): # Would be Protocol instead of object in real code 

    def dataReceived(self, data): 
     req = json.loads(data) # create a dictionary from JSON string 
     modname, funcname = req['method'].split('.') 
     m = __import__(modname) 
     method = getattr(m, funcname) # get the method 
     method(self, req['params']) # call the method 

En supposant que vous l'essayer comme si nous avons exécuté ceci:

mp = MyProtocol() 
mp.dataReceived('{"method":"somemod.add", "params":[1,2,3]}') 

Vous Wold avoir un module somemod.py dans le même répertoire que l'exemple avec le contenu suivant (en miroir votre exemple méthode .add() ci-dessus):

import json 

def add(proto, params): 
    result = {} # initialize a dictionary to convert later to JSON 
    result['result'] = sum(params) 
    result['error'] = None 
    result['id'] = 1 
    proto.transport.write(json.dumps(result)) # return a JSON string 
    proto.transport.loseConnection() # close connection 

Cela vous permet d'avoir un module par méthode servie. L'appel method(.. ci-dessus transmettra toujours votre instance MyProtocol au service appelable. (Si vous voulez vraiment des méthodes d'instance, voici les instructions pour ajouter des méthodes en utilisant python: http://irrepupavel.com/documents/python/instancemethod/)

Vous aurez besoin de beaucoup de gestion des erreurs. Par exemple, vous devez vérifier beaucoup d'erreurs à l'appel split() sur la ligne 2 de dataReceived(). Avec cette option, vous pouvez avoir des fichiers séparés avec une fonction pour chaque méthode que vous devez prendre en charge. En aucun cas un exemple complet, mais cela pourrait vous aider, car ce que vous cherchez est assez complexe.

Pour un enregistrement plus formel, je vous recommande un dict à MyProtocol avec les noms des méthodes que vous soutenez, le long des lignes de:

# in MyProtocol's __init__() method: 
self.methods = {} 

et une méthode de registre ..

def register(self, name, callable): 
    self.methods[name] = callable 

..modify dataReceived() ..

def dataReceived(self, data): 
    # ... 
    modname, funcname = self.methods.get(req['method'], False) 
    # ..continue along the lines of the dataReceived() method above 

Q Résumé rapide d'un post trop long: la fonction __import__ (http://docs.python.org/library/functions.html) sera très certainement un élément clé de votre solution.

+0

Un grand merci pour votre réponse. Je ne sais pas ce que je finirai par faire, mais votre approche est ingénieuse et me met sur la bonne voie. – Escualo

+0

Pour le moment, votre réponse fonctionne très bien. Merci beaucoup! – Escualo