为xmlrpclib.ServerProxy设置超时时间。

26
我正在使用xmlrpclib.ServerProxy来调用远程服务器上的RPC。如果没有与服务器的网络连接,它会花费默认的10秒来返回socket.gaierror给我的程序。
在没有网络连接的情况下进行开发或者远程服务器宕机时,这是很烦人的。有没有办法更新我 ServerProxy 对象的超时时间?
我看不到明显的方法来获取访问套接字并进行更新。
9个回答

22

还有一个更直接的解决方案在这里: http://www.devpicayune.com/entry/200609191448

import xmlrpclib 
import socket

x = xmlrpclib.ServerProxy('http:1.2.3.4')  
socket.setdefaulttimeout(10)        #set the timeout to 10 seconds 
x.func_name(args)                   #times out after 10 seconds
socket.setdefaulttimeout(None)      #sets the default back

套接字如何应用于xmlrpclib?我们从未在x上设置套接字。 - ashchristopher
1
@ashchristopher:我还没有测试过这段代码,但它看起来像是 setdefaulttimeout 是一个全局默认值。因此,如果 xmlrpclib 没有设置任何特定的超时值,它可能会使用全局默认值。 - MiniQuark
1
@MiniQuark:正确,它是一个全局默认值(None),这意味着新的套接字对象没有超时。 setdefaulttimeout函数的优点在于它适用于HTTPS / SSL套接字,而其他发布的示例仅适用于HTTP。 - Jeff Bauer
@Jeff Bauer:使用HTTP的社区维基示例也可以通过子类化HTTPSConnection、HTTPS和SafeTransport来完成。这应该可以完全相同地工作。 - housemaister
1
毫无疑问,这是我代码的胜利者。相比于其他人建议添加3个类,我只需添加2行代码就完成了。顺便说一下,这是TL;DR(太长不读)的版本。 - Giszmo

18

清洁的非全局版本。

import xmlrpclib
import httplib


class TimeoutHTTPConnection(httplib.HTTPConnection):
    def connect(self):
        httplib.HTTPConnection.connect(self)
        self.sock.settimeout(self.timeout)


class TimeoutHTTP(httplib.HTTP):
    _connection_class = TimeoutHTTPConnection

    def set_timeout(self, timeout):
        self._conn.timeout = timeout


class TimeoutTransport(xmlrpclib.Transport):
    def __init__(self, timeout=10, *l, **kw):
        xmlrpclib.Transport.__init__(self, *l, **kw)
        self.timeout = timeout

    def make_connection(self, host):
        conn = TimeoutHTTP(host)
        conn.set_timeout(self.timeout)
        return conn


class TimeoutServerProxy(xmlrpclib.ServerProxy):
    def __init__(self, uri, timeout=10, *l, **kw):
        kw['transport'] = TimeoutTransport(
            timeout=timeout, use_datetime=kw.get('use_datetime', 0))
        xmlrpclib.ServerProxy.__init__(self, uri, *l, **kw)


if __name__ == "__main__":
    s = TimeoutServerProxy('http://127.0.0.1:9090', timeout=2)
    s.dummy()

对我来说完美无缺,Python2.5在Debian上运行良好。顺便提一下,你的导入语句中有一个多余的'.'字符,并且'import socket'没有被使用。 - Jonathan Hartley
5
在Python 2.7上无法工作。当我进行RPC调用时,出现错误AttributeError:TimeoutHTTP实例没有'getresponse'属性 - AFoglia
当我迁移到Python3时,出现了“AttributeError: module 'http.client' has no attribute 'HTTPClient'”错误。 - LetsOMG

5

我希望得到一个简洁明了的版本,基于其他回答的内容,我想出了以下翻译:

import xmlrpclib


class TimeoutTransport(xmlrpclib.Transport):

    def __init__(self, timeout, use_datetime=0):
        self.timeout = timeout
        # xmlrpclib uses old-style classes so we cannot use super()
        xmlrpclib.Transport.__init__(self, use_datetime)

    def make_connection(self, host):
        connection = xmlrpclib.Transport.make_connection(self, host)
        connection.timeout = self.timeout
        return connection


class TimeoutServerProxy(xmlrpclib.ServerProxy):

    def __init__(self, uri, timeout=10, transport=None, encoding=None, verbose=0, allow_none=0, use_datetime=0):
        t = TimeoutTransport(timeout)
        xmlrpclib.ServerProxy.__init__(self, uri, t, encoding, verbose, allow_none, use_datetime)


proxy = TimeoutServerProxy(some_url)

起初我没有意识到xmlrpclib使用的是旧式类,因此在评论中发现这很有用,否则一切都应该非常自我解释。

我不明白为什么还必须对httplib.HTTP进行子类化,如果有人能给我解释一下,请告诉我。上述解决方案已经尝试并且有效。


2

这里是一段可以在Python 2.7上运行的代码(可能适用于其他2.x版本的Python),不会出现"AttributeError, instance has no attribute 'getresponse'"错误。


class TimeoutHTTPConnection(httplib.HTTPConnection):
    def connect(self):
        httplib.HTTPConnection.connect(self)
        self.sock.settimeout(self.timeout)

class TimeoutHTTP(httplib.HTTP):
    _connection_class = TimeoutHTTPConnection

    def set_timeout(self, timeout):
        self._conn.timeout = timeout

class TimeoutTransport(xmlrpclib.Transport):
    def __init__(self, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, *args, **kwargs):
        xmlrpclib.Transport.__init__(self, *args, **kwargs)
        self.timeout = timeout

    def make_connection(self, host):
        if self._connection and host == self._connection[0]:
            return self._connection[1]

        chost, self._extra_headers, x509 = self.get_host_info(host)
        self._connection = host, httplib.HTTPConnection(chost)
        return self._connection[1]


transport = TimeoutTransport(timeout=timeout)
xmlrpclib.ServerProxy.__init__(self, uri, transport=transport, allow_none=True)

看起来像是不完整的代码片段。 - Denis Barmenkov

1

基于antonylesuisse的版本,这是一个可以在Python 2.6及以上版本中运行的可用版本。

# -*- coding: utf8 -*-
import xmlrpclib
import httplib
import socket

class TimeoutHTTP(httplib.HTTP):
   def __init__(self, host='', port=None, strict=None,
                timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
        if port == 0:
            port = None
        self._setup(self._connection_class(host, port, strict, timeout))

class TimeoutTransport(xmlrpclib.Transport):
    def __init__(self, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, *args, **kwargs):
        xmlrpclib.Transport.__init__(self, *args, **kwargs)
        self.timeout = timeout

    def make_connection(self, host):
        host, extra_headers, x509 = self.get_host_info(host)
        conn = TimeoutHTTP(host, timeout=self.timeout)
        return conn

class TimeoutServerProxy(xmlrpclib.ServerProxy):
    def __init__(self, uri, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
                 *args, **kwargs):
        kwargs['transport'] = TimeoutTransport(timeout=timeout,
                                    use_datetime=kwargs.get('use_datetime', 0))
        xmlrpclib.ServerProxy.__init__(self, uri, *args, **kwargs)

1

这里是http://code.activestate.com/recipes/473878/的逐字复制:

def timeout(func, args=(), kwargs={}, timeout_duration=1, default=None):
    import threading
    class InterruptableThread(threading.Thread):
        def __init__(self):
        threading.Thread.__init__(self)
        self.result = None

        def run(self):
            try:
                self.result = func(*args, **kwargs)
            except:
                self.result = default

    it = InterruptableThread()
    it.start()
    it.join(timeout_duration)
    if it.isAlive():
        return default
    else:
        return it.result

1
这是另一种聪明而非常“Pythonic”的解决方案,使用Python的with语句:
import socket
import xmlrpc.client

class MyServerProxy:
    def __init__(self, url, timeout=None):
        self.__url = url
        self.__timeout = timeout
        self.__prevDefaultTimeout = None

    def __enter__(self):
        try:
            if self.__timeout:
                self.__prevDefaultTimeout = socket.getdefaulttimeout()
                socket.setdefaulttimeout(self.__timeout)
            proxy = xmlrpc.client.ServerProxy(self.__url, allow_none=True)
        except Exception as ex:
            raise Exception("Unable create XMLRPC-proxy for url '%s': %s" % (self.__url, ex))
        return proxy
    def __exit__(self, type, value, traceback):
        if self.__prevDefaultTimeout is None:
            socket.setdefaulttimeout(self.__prevDefaultTimeout)

这个类可以像这样使用:

with MyServerProxy('http://1.2.3.4', 20) as proxy:
    proxy.dummy()

我认为在这里使用__exit __()是错误的-它不应该将以前的超时值测试为None,它只应该设置先前存在的任何内容(无论是浮点数还是None)。 - Martin Pecka
@MartinPecka 我同意,我也认为在 __exit__() 中它应该只是将 setdefaulttimeout 设置为 __prevDefaultTimeout。此外,我认为 self.__prevDefaultTimeout = socket.getdefaulttimeout() 应该移到 if 上面,就像 __enter__ 的第一行一样。 - RcoderNY

1

基于antonylesuisse的代码,但适用于Python 2.7.5,解决了问题:AttributeError: TimeoutHTTP instance has no attribute 'getresponse'

class TimeoutHTTP(httplib.HTTP):
    def __init__(self, host='', port=None, strict=None,
                timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
        if port == 0:
            port = None
        self._setup(self._connection_class(host, port, strict, timeout))

    def getresponse(self, *args, **kw):
        return self._conn.getresponse(*args, **kw)

class TimeoutTransport(xmlrpclib.Transport):
    def __init__(self,  timeout=socket._GLOBAL_DEFAULT_TIMEOUT, *l, **kw):
        xmlrpclib.Transport.__init__(self, *l, **kw)
        self.timeout=timeout

    def make_connection(self, host):
        host, extra_headers, x509 = self.get_host_info(host)
        conn = TimeoutHTTP(host, timeout=self.timeout)
        return conn

class TimeoutServerProxy(xmlrpclib.ServerProxy):
    def __init__(self, uri, timeout= socket._GLOBAL_DEFAULT_TIMEOUT, *l, **kw):
        kw['transport']=TimeoutTransport(timeout=timeout, use_datetime=kw.get('use_datetime',0))
        xmlrpclib.ServerProxy.__init__(self, uri, *l, **kw)

proxy = TimeoutServerProxy('http://127.0.0.1:1989', timeout=30)
print proxy.test_connection()

0

以下示例适用于Python 2.7.4。

import xmlrpclib
from xmlrpclib import *
import httplib

def Server(url, *args, **kwargs):
    t = TimeoutTransport(kwargs.get('timeout', 20))
    if 'timeout' in kwargs:
       del kwargs['timeout']
    kwargs['transport'] = t
    server = xmlrpclib.Server(url, *args, **kwargs)
    return server

TimeoutServerProxy = Server

class TimeoutTransport(xmlrpclib.Transport):

    def __init__(self, timeout, use_datetime=0):
        self.timeout = timeout
        return xmlrpclib.Transport.__init__(self, use_datetime)

    def make_connection(self, host):
        conn = xmlrpclib.Transport.make_connection(self, host)
        conn.timeout = self.timeout
        return connrpclib.Server(url, *args, **kwargs)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接