Я создал простой сервер RPC для выполнения определенных задач, общих для наших команд, но вызываемых из разных сетей. Сервер выглядит так (я не включаю обработку ошибок для краткости):
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()
Это очень просто: сервер получает запрос JCON RPC от клиента, ищет метод и вызывает метод, передающий параметры. Сам метод является тем, который возвращает ответ JSON RPC. Для менее знакомого, JSON RPC выглядит примерно так:
request:
{"method":"my_method", "params":[1,2,3], "id":"my_id"}
response:
{"result":"my_result", "error":null, "id":"my_id"}
Сервер RPC, как и я, очень хорошо выполняет мои текущие цели (как вы можете себе представить, моя задача очень проста). Но мне нужно будет продолжать добавлять методы по мере увеличения сложности задачи.
Я не хочу открывать основной файл и добавлять еще один def method3(...)
, а через две недели добавить def method4(...)
и т.д.; код будет расти слишком быстро, и обслуживание будет сложнее и сложнее.
Итак, мой вопрос: как я могу создать архитектуру, которая позволяет мне регистрировать методы на сервере. Бонус должен состоять в том, чтобы иметь отдельную папку, содержащую один файл для каждого метода, чтобы они могли легко использоваться и поддерживаться. Эта "архитектура" также позволит мне отложить поддержание некоторых методов кому-то другому, независимо от их понимания Twisted.
Мне все равно, если мне нужно перезапустить сервер каждый раз, когда будет зарегистрирован новый метод, но очевидный плюс будет, если у меня тоже нет:).
Спасибо.
Немного большой порядок;), но здесь есть некоторые начальные шаги для вас (очень сильно издевавшиеся, скрученные особенности, омментированные в примерах):
# 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
Предполагая, что вы попробуете это, как если бы мы выполнили это:
mp = MyProtocol()
mp.dataReceived('{"method":"somemod.add", "params":[1,2,3]}')
У вас есть модуль somemod.py
в том же каталоге, что и пример со следующим содержимым (зеркалирование вашего примера метода .add()
выше):
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
Это позволяет использовать один модуль для каждого метода. Вышеуказанный вызов method(..
всегда будет передавать ваш экземпляр MyProtocol
на вызывающий вызов. (Если вам действительно нужны методы экземпляра, вот инструкции по добавлению методов с использованием python: http://irrepupavel.com/documents/python/instancemethod/)
Вам понадобится много обработки ошибок. Например, вам нужно много ошибок при вызове split()
в строке 2 dataReceived()
.
С этим вы можете иметь отдельные файлы с одной функцией в них для каждого метода, который вам нужно поддерживать. Ни в коем случае не полный пример, но он может заставить вас идти, поскольку то, что вы ищете, довольно сложно.
Для более формальной регистрации я бы рекомендовал dict
в MyProtocol
с именами поддерживаемых вами методов в соответствии с:
# in MyProtocol __init__() method:
self.methods = {}
И метод регистрации.
def register(self, name, callable):
self.methods[name] = callable
.. изменить dataReceived()
..
def dataReceived(self, data):
# ...
modname, funcname = self.methods.get(req['method'], False)
# ..continue along the lines of the dataReceived() method above
Краткая сводка слишком длинного сообщения: функция __import__
(http://docs.python.org/library/functions.html), безусловно, будет ключевой частью вашего решение.