3

我有以下情况:

我有一个 Web 服务,可根据单个用户请求聚合来自某些第三方服务器的数据。对第三方的请求可以是 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 问题,发现它使用 httplib,而 httplib深入使用 socket.makefile(),文档说:

socket.makefile([mode[, bufsize]])

返回与套接字关联的文件对象。(文件对象在文件 > 对象中描述。)文件对象引用套接字文件描述符的 dup()ped 版本,因此 > 文件对象和套接字对象可以独立关闭或垃圾收集。套接字必须处于阻塞模式(不能有超时)。可选模式和 bufsize 参数的解释方式与内置 file() 函数相同。

我发现的所有其他 SOAP 库,都以一种或另一种方式使用 httplib。为了使事情复杂化,我可能需要从请求线程访问数据库,我不完全理解用这种策略杀死线程的后果是什么,我正在考虑从线程外部做数据库的东西,当那个是可能的。

然后,我的问题是:

当不遵守超时时,我的 Web 服务如何及时响应用户并优雅地处理行为不良的第三方服务器?


HTTPResponse 使用 makefile 的事实可能没有我想象的那么糟糕,事实证明默认情况下它makefile实际上是非缓冲的,并且它可以引发超时异常,这是我尝试过的:

在一个控制台上,我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

但是我如何做可靠的第三方请求的问题仍然存在。

4

1 回答 1

0

我想我设法建立了一个切实可行的解决方案。

我要做的第一件事是启动将请求任何需要的第三方服务器的线程。这很好用,因为当线程正在执行阻塞操作(socket.recv() 就此而言)时 GIL 不会保持,这允许我的服务器在处理请求时做自己的事情。

我从线程中删除了所有副作用,不再与数据库交谈,如果请求的响应时间超出预期,我不需要杀死它,只需保留它并忽略它。

当第一个线程启动时启动一个计时器,在我的服务器完成它之后它绝对需要第三方结果它检查每个线程以查看它们是否完成,当它们全部完成或超时时它会得到结果每个完成的线程,它看起来像这样:

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())
于 2013-08-20T22:23:40.320 回答