我有以下场景:
我有一个网络服务,可以根据单个用户请求从某些第三方服务器聚合数据。对第三方的请求可以是 SOAP 或带有 XML 数据的普通 urllib2 请求,并且每个请求都在一个单独的线程中完成。
这是我正在做的事情的总体情况:
ThirdParty1(Thread):
def run(self):
try:
result = SOAPProxy('http://thirdparty.com', timeout=2).method(params)
dostuff_and_save(result) # save results on database
except Exception:
log.warn('Ooops')
ThirdParty2(Thread): ...
def myview(params):
thread = [ThirdParty1(), ThirdParty2()]
for t in thread: t.start()
for t in thread: t.join(timeout=2)
return result # this is actually just a token, that I use to retrieve the data saved by the threads
我当前的问题是当任何第三方服务器在他们这边挂起时可靠地返回对我用户请求的响应。我尝试在 SOAPProxy 对象的线程连接上设置超时,并执行 socket.setdefaulttimeout
。没有遵守任何超时。
我设法深入挖掘 SOAPProxy 问题并发现它 uses httplib和 httplib deep down uses socket.makefile() ,文档说:
socket.makefile([mode[, bufsize]])
Return a file object associated with the socket. (File objects are described in File > Objects.) The file object references a dup()ped version of the socket file descriptor, so > the file object and socket object may be closed or garbage-collected independently. The socket must be in blocking mode (it can not have a timeout). The optional mode and bufsize arguments are interpreted the same way as by the built-in file() function.
我发现的每个其他 SOAP 库,都以某种方式使用 httplib。使事情复杂化的是,我可能需要从请求线程访问数据库并且我不完全理解用 this sort of strategy 终止线程的后果是什么,我正在考虑在可能的情况下从线程外部执行数据库操作。
那么,我的问题是:
我的 Web 服务如何及时响应用户并在不遵守超时时优雅地处理行为不当的第三方服务器?
HTTPResponse 使用 makefile 的事实可能没有我想象的那么糟糕,事实证明 makefile
确实是 non buffering默认情况下,它可以引发超时异常,这是我尝试过的:
在一个控制台上,我打开了 netcat -l -p 8181 '0.0.0.0'
,在另一个控制台上,我打开了 python2.7
并运行了:
>>> import socket
>>> af, socktype, proto, canoname, sa = socket.getaddrinfo('0.0.0.0', 8181, 0, socket.SOCK_STREAM)[0]
>>> s=socket.socket(af, socktype, proto)
>>> s.settimeout(.5)
>>> s.connect(sa)
>>> f=s.makefile('rb', 0)
>>> f.readline()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/socket.py", line 430, in readline
data = recv(1)
socket.timeout: timed out
但是我的如何进行可靠的第三方请求的问题仍然存在。
最佳答案
我认为我设法构建了一个可靠的解决方案。
我做的第一件事是启动将请求所需的任何第三方服务器的线程。这很好用,因为在线程执行阻塞操作时 GIL 没有保持(就此而言,socket.recv()),这允许我的服务器在处理请求时做它自己的事情。
我从线程中移除了所有副作用,不再与数据库对话,如果一个请求的响应时间超过预期,我不需要终止它,让它保持原状并忽略它。
当第一个线程启动时,计时器启动,在我的服务器完成它之后它绝对需要第三方结果它检查每个线程以查看它们是否完成,当它们全部完成或超时时它得到每个完成线程的结果,看起来像这样:
start, data = time(), []
threads = launch_threads()
# ... do my thing
for t in threads: # wait up to TIMEOUT
timeout = TIMEOUT - (time() - start)
t.join(t)
for t in threads:
if not t.isAlive(): # should not have a race
data.append(t.getdata())
关于multithreading - 如何及时响应用户,优雅的处理第三方服务器的不良行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15419915/