【问题标题】:XML-RPC returning value before executing all function code执行所有函数代码之前的 XML-RPC 返回值
【发布时间】:2011-08-18 11:09:06
【问题描述】:

我有 XML-RPC 服务器:

import time

import xmlrpclib
from SimpleXMLRPCServer import SimpleXMLRPCServer

class Worker(object):
    def start_work(self):
        # is it possible do return value to client here?
        self.do_work()
        return 'we started!'

    def do_work(self):
        while(True):
            print 'I\'m doing work...'
            time.sleep(3)


if __name__ == '__main__':
    port = 8080
    server = SimpleXMLRPCServer(("localhost", port))
    print "Listening on port %s..." % port

    w = Worker()

    server.register_function(w.start_work)
    while(1):
        server.handle_request()

# vim: filetype=python syntax=python expandtab shiftwidth=4 softtabstop=4 encoding=utf8

简单的客户端:

import xmlrpclib
c = xmlrpclib.ServerProxy('http://localhost:8080/')
print c.start_work()

当然,start_work 函数返回的值永远不会被打印出来。

我的问题是如何在完成工作之前重写服务器代码以使返回值成为可能。我知道我可以为此使用线程,但我想确定没有更简单的方法。

【问题讨论】:

    标签: python multithreading xml-rpc xmlrpclib simplexmlrpcserver


    【解决方案1】:

    如果您希望 XML-RPC 具有长时间运行、提前返回的任务,您可能需要将服务器重写为异步框架,例如 twisted

    【讨论】:

    • 我对此有点困惑。 twisted 的文档非常大,我找不到这样的例子。你能提供一个吗?
    • 基本上,您会按照链接的示例进行操作,但是要在返回后做更多的工作,您会做一些twisted.internet.reactor.callLater(more_work_callback)的效果
    【解决方案2】:

    我不确定在没有线程或不重新实现 SimpleXMLRPCServer 的某些部分或不破坏 XMLRPC 协议的情况下做这样的事情是一个好主意,但是您可以通过在您的调用方法:

    class Worker(object):
        def start_work(self):
            # is it possible do return value to client here?
            # yes, with a yield
            yield "the_return_value"
            self.do_work()
            # but yielding a first value and returning it to client will disable any next response
            # return 'we started!'
    
        def do_work(self):
            while(True):
                print 'I\'m doing work...'
                time.sleep(3)
    

    并覆盖 SimpleXMLRPCRequestHandler 的 do_POST 方法以调用两次被调用的方法(### 部分周围的所有内容都是 python 代码的标准部分)

    class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
    
        def do_POST(self):
            """Handles the HTTP POST request.
            Attempts to interpret all HTTP POST requests as XML-RPC calls,
            which are forwarded to the server's _dispatch method for handling.
            """
            # Check that the path is legal
            if not self.is_rpc_path_valid():
                self.report_404()
                return
    
            try:
                # Get arguments by reading body of request.
                # We read this in chunks to avoid straining
                # socket.read(); around the 10 or 15Mb mark, some platforms
                # begin to have problems (bug #792570).
                max_chunk_size = 10*1024*1024
                size_remaining = int(self.headers["content-length"])
                L = []
                while size_remaining:
                    chunk_size = min(size_remaining, max_chunk_size)
                    L.append(self.rfile.read(chunk_size))
                    size_remaining -= len(L[-1])
                data = ''.join(L)
    
                # In previous versions of SimpleXMLRPCServer, _dispatch
                # could be overridden in this class, instead of in
                # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
                # check to see if a subclass implements _dispatch and dispatch
                # using that method if present.
                response = self.server._marshaled_dispatch(
                        data, getattr(self, '_dispatch', None)
                    )
                ##############################################
                ##############################################
                ## Here you'll have the first part of your response
                response = response.next() # to get the first item of the generator (yes, a little bit uggly)
                ##############################################
                ##############################################
            except Exception, e: # This should only happen if the module is buggy
                # internal error, report as HTTP server error
                self.send_response(500)
    
                # Send information about the exception if requested
                if hasattr(self.server, '_send_traceback_header') and \
                        self.server._send_traceback_header:
                    self.send_header("X-exception", str(e))
                    self.send_header("X-traceback", traceback.format_exc())
    
                self.end_headers()
            else:
                # got a valid XML RPC response
                self.send_response(200)
                self.send_header("Content-type", "text/xml")
                self.send_header("Content-length", str(len(response)))
                self.end_headers()
                self.wfile.write(response)
    
                # shut down the connection
                self.wfile.flush()
                self.connection.shutdown(1)
    
                ##############################################
                ##############################################
                ## Here you've send the the first part of your response to the client, relaunch the method
                self.server._marshaled_dispatch(
                        data, getattr(self, '_dispatch', None)
                )
                ##############################################
                ##############################################
    

    然后,main 会变成:

    if __name__ == '__main__':
        port = 8080
        server = SimpleXMLRPCServer(("localhost", port), requestHandler=MySimpleXMLRPCRequestHandler)
        print "Listening on port %s..." % port
    
        w = Worker()
    
        server.register_function(w.start_work)
        while(1):
            server.handle_request()
    

    【讨论】:

    • 看起来 yield 不能与 xmlrpc 一起工作:xmlrpclib.Fault: :cannot marshal objects">跨度>
    • @Adam :哦,是的,yield 返回一个生成器对象,尝试在第一个响应对象上使用response = response.next() 调用。
    • 我不知道那是什么意思...你能举个例子吗?
    • 很有可能,如果您的应用程序是针对企业用途的,那么随着它的成熟,您会发现这并不能完全满足您的需求。只需使用 Twisted。它不是一个非常复杂的框架,您必须学习数周才能开始使用。平易近人。
    猜你喜欢
    • 2021-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-08
    • 2020-04-05
    • 1970-01-01
    • 1970-01-01
    • 2015-06-29
    相关资源
    最近更新 更多