一、背景:
我们都知道,RPC本质是一个代理模式,是在HTTP或HTTPS请求上面做的封装,那么别人封装好了,拿过来用就好了。这样带来了极大的遍历,但也就导致了另外的问题,有的时候就是不够灵活。在python项目X山中,有的地方用了xmlrpc.client , 但又缺少超时机制。
二、分析
直接上代码了
import xmlrpc.client
url = 'http://{}:{}'.format("127.0.0.1", 5678)
client = xmlrpc.client.ServerProxy(url)
client.f11()
点进去看看吧。在短短三行代码中,我们只能够发现,只有后两行,有机会传一个超时参数,再一看,倒数第一行,是自己定义的,那压力来到了xmlrpc.client.ServerProxy(url)
class ServerProxy:
def __init__(self, uri, transport=None, encoding=None, verbose=False,
allow_none=False, use_datetime=False, use_builtin_types=False,
*, headers=(), context=None):
# establish a "logical" server connection
# get the url
type, uri = urllib.parse._splittype(uri)
if type not in ("http", "https"):
raise OSError("unsupported XML-RPC protocol")
self.__host, self.__handler = urllib.parse._splithost(uri)
if not self.__handler:
self.__handler = "/RPC2"
if transport is None:
if type == "https":
handler = SafeTransport
extra_kwargs = {"context": context}
else:
handler = Transport
extra_kwargs = {}
transport = handler(use_datetime=use_datetime,
use_builtin_types=use_builtin_types,
headers=headers,
**extra_kwargs)
self.__transport = transport
self.__encoding = encoding or 'utf-8'
self.__verbose = verbose
self.__allow_none = allow_none
这个构造函数,把眼睛看花也看不到timeout字样,于是搜了搜看到一种解决方案
xmlrpclib客户端请求超时-python黑洞网
这老哥说到人心坎,全局超时影响过于大了,但给的是python2的例子,而且包名和我的报名也有区别。
?
此事回头再看transport参数,
发现这个构造方法里面也确实只有这个参数能掀起一些波澜(这个结论存在马后炮成分),并且再构造方法里面出现了同名的类:Transport
因此,顺着继承重写方法的路往下走
Transport 类
代码就不全部粘贴了,搞一部分下来
##
# Standard transport class for XML-RPC over HTTP.
# <p>
# You can create custom transports by subclassing this method, and
# overriding selected methods.
class Transport:
? ?
def make_connection(self, host):
#return an existing connection if possible. This allows
#HTTP/1.1 keep-alive.
if self._connection and host == self._connection[0]:
return self._connection[1]
# create a HTTP connection object from a host descriptor
chost, self._extra_headers, x509 = self.get_host_info(host)
self._connection = host, http.client.HTTPConnection(chost)
return self._connection[1]
class HTTPConnection:
def __init__(self, host, port=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
source_address=None, blocksize=8192): ?
关键点1:类前面的备注这里提示我们可以去继承这个类 关键点2:方法里面的HTTPConnection也存在timeout参数
?
继续顺着这个思路往下,咱们看看socket._GLOBAL_DEFAULT_TIMEOUT到底是个啥
这就是个OBJECT? ????????按照我的预期,应该是个数字.......?
给他打印出来看看
到这里,我觉得可能不用再追了,我的目标是解决问题,当你设置为_GLOBAL_DEFAULT_TIMEOUT, 那说明就是无限等待的。
三、实践
就按照这个思路,重写了Transport类
客户端思路如下
import http.client
import xmlrpc.client
class TimeoutTransport(xmlrpc.client.Transport):
time_out = 60 # 单位:秒
def make_connection(self, host):
# return an existing connection if possible. This allows
# HTTP/1.1 keep-alive.
if self._connection and host == self._connection[0]:
return self._connection[1]
# create a HTTP connection object from a host descriptor
chost, self._extra_headers, x509 = self.get_host_info(host)
self._connection = host, http.client.HTTPConnection(chost, timeout=self.time_out)
return self._connection[1]
def set_timeout(self, timeout):
self.time_out = timeout
url = 'http://{}:{}'.format("127.0.0.1", 5678)
try:
client = xmlrpc.client.ServerProxy(url, transport=TimeoutTransport())
client.f11()
except Exception as e:
print(e)
print("--------------------------")
try:
client2 = xmlrpc.client.ServerProxy(url)
client2.f11()
except Exception as e:
print(e)
import sys
import time
from socketserver import ThreadingMixIn
from xmlrpc.server import SimpleXMLRPCServer
class ThreadXMLRPCServer(ThreadingMixIn, SimpleXMLRPCServer):
name = "xml-server"
def f11():
print("老子睡了")
time.sleep(10000)
print("老子醒了")
return 11
server = ThreadXMLRPCServer(('0.0.0.0', 5678))
server.register_function(f11, "f11")
server.serve_forever()
四、测试
server日志
客户端日志:
若是不手动停止,怕是永远也等不到了呢~
?
?
|