如何及时响应用户,优雅地处理行为不端的第三方服务器



我有以下场景:

我有一个web服务,根据单个用户请求聚合来自某些第三方服务器的数据。对第三方的请求可以是带有XML数据的SOAP或普通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(),文档说:

插座。makefile([模式[,bufsize]])

返回与套接字关联的文件对象。(文件对象在File>对象)。文件对象引用了一个dup()ped版本的套接字文件描述符,因此>文件对象和套接字对象可以分别被关闭或被垃圾收集。套接字必须处于阻塞模式(不能有超时)。可选的mode和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

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

我想我找到了一个可行的解决方案。

我要做的第一件事是启动线程,它将请求所需的任何第三方服务器。这工作得很好,因为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())

相关内容

  • 没有找到相关文章

最新更新