【问题标题】:Adding methods to a simple RPC server in a clean and separated way以干净和分离的方式向简单的 RPC 服务器添加方法
【发布时间】:2011-03-12 23:55:47
【问题描述】:

我创建了一个简单的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()

这很简单:服务端接收到客户端的JSON 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 的理解如何。

我不在乎是否每次注册新方法时都需要重新启动服务器,但如果我没有也有一个明显的优势:)。

谢谢。

【问题讨论】:

    标签: python architecture twisted network-protocols json-rpc


    【解决方案1】:

    有点大的顺序 ;) 但这里有一些初始步骤(在示例中省略了非常严重的模拟、扭曲的细节):

    # 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/

    您将需要添加大量错误处理。例如,您需要在 dataReceived() 的第 2 行的 split() 调用中进行大量错误检查。

    有了它,您可以为需要支持的每种方法创建包含一个函数的单独文件。这绝不是一个完整的示例,但它可能会让您继续前进,因为您要查找的内容非常复杂。

    对于更正式的注册,我建议在 MyProtocol 中使用 dict,其中包含您支持的方法名称,大致如下:

    # in MyProtocol's __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) 肯定会成为您解决方案的关键部分。

    【讨论】:

    • 非常感谢您的回答。我不知道我最终会做什么,但你的方法很巧妙,让我走上了正轨。
    • 目前,您的答案功能非常好。非常感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-13
    • 2014-11-30
    • 1970-01-01
    • 1970-01-01
    • 2011-03-12
    • 1970-01-01
    相关资源
    最近更新 更多