【问题标题】:Abort a GET request in Python when the server is not responding当服务器没有响应时,在 Python 中中止 GET 请求
【发布时间】:2015-11-07 00:32:35
【问题描述】:

我在 Python 中实现了一个 HTTP 长轮询器。简单来说,它是一个定期连接到服务器并通过 GET 请求检索一些信息的程序。 长轮询技术与“正常”轮询不同,因为如果服务器接收到请求并且它没有新数据,它不会发送空响应,而是等待新数据可用,从而使请求保持打开状态。

基本上,当我向该服务器发出请求时,它可能会立即向我发送回复,或者可能会使我的 GET 保持打开几分钟。

一切正常,当我希望我的程序关闭时,问题就出现了。我已经尝试了urllib3requests 模块,如果有挂起请求,它们只是不让我的程序关闭。

所以,我的问题是是否有办法在服务器没有响应时中止 GET 请求(使用提到的模块)。 在这些情况下,设置超时可以解决我的问题,但在这种特定情况下显然是不可能的。

一种解决方案可能是将请求放入线程中并在程序关闭时将其终止,但终止线程并不是一个好习惯。 也许有一个更指示的模块来实现长轮询?

【问题讨论】:

  • "killing a thread isn't good practice" 是什么意思?
  • Python 不会让你杀死一个线程,据我所知,这通常被认为是一种不好的做法。在 Python 中实现此目的的唯一方法是使用 multiprocessing 模块。

标签: python http get long-polling


【解决方案1】:

我建议你在线程中使用urllib2(我没有看到任何其他方式),然后在等待响应时关闭连接。

其他方法都可以解决问题,但在某种意义上不是你想要的。

一个是你总是从服务器返回数据,但是当你没有信息返回时,返回一些消息表明客户端应该稍后再检查。

你提到的一个是杀死线程,但是,这不是很好的解决方案。

我建议关闭与服务器的连接并等待它中断:

from urllib2 import urlopen
from thread import start_new_thread as thread
from time import sleep

class Req:
    def __init__ (self, url, callback=lambda: None):
        self.polled_conn = None
        self.url   = url
        self.data  = None
        self.error = None
        self.callback = callback
        self.cancelled = 0
        self.polling = 0

    def poll (self):
        thread(self._poll,())

    def get (self):
        self.data  = None
        self.error = None
        cb = self.callback
        self.callback = lambda: None
        thread(self._poll,())
        while self.data==None and self.error==None: sleep(0.001)
        self.callback = cb
        if self.error: raise self.error
        return self.data

    def _poll (self):
        if self.polling: return
        self.polling = 1
        self.data  = None
        self.error = None
        self.cancelled = 0
        try:
            self.polled_conn = u = urlopen(self.url)
        except Exception, e: self.error = e; self.polling = 0; return self.callback()
        try:
            self.data = u.read()
        except AttributeError, e:
            if "recv" in str(e): self.cancelled = 1; self.polling = 0; return # Receiving aborted!
            else: self.error = e
        except IOError, e: self.cancelled = 1; self.polling = 0; return # Receiving aborted
        except Exception, e: self.error = e
        self.polling = 0
        self.callback()

    def cancel (self):
        if self.polled_conn:
            self.polled_conn.close()

    def iscancelled (self): return self.cancelled

通过 get() 方法提供了一些用法,但您有更多的可能性。 使用 get() 你有标准的数据阻塞返回:

r = Req("http://example.com")
data = r.get()
print data

为了实现你想要的,你可以在这里指定一个回调并用它来处理数据,在程序退出时你可以取消任何挂起的传输:

def oncallback ():
    if r.data!=None:
        print len(r.data), r.data[:100]
    else: raise r.error
    sleep(1)
    r.poll() # Poll again for new info

r = None
def start ():
    global r
    r = Req("http://example.com", oncallback)
    r.poll()

start()
raw_input() # Wait for enter to close
r.cancel()
print "Cancelling"
if r.polling:
    while not r.iscancelled(): sleep(0.001)
print "Ready to close!"
# You can set r.polling to 1, to prevent the callback to poll again in midle of shutdown
#r.polling = 1 # Dirty trick
# Using threading module would make things just a little easier

或者您仍然可以在没有回调的情况下执行此操作(类似异步):

running = 1
r = Req("http://example.com")
r.poll()
while running:
    if r.error!=None:
        print r.error
        # Decide what to do according to this error
        #if not isinstance(r.error, URLError): r.poll() # or something
        continue
    if r.data==None: sleep(0.001); continue # Do something anyway while waiting for data
    # Data arrived so:
    print data[:100] # Do some stuff
    r.poll() # Poll again for new
    # Somewhere here check whether user wants to close a program and then
    #running = 0 or break

r.cancel()
if r.polling:
    while not r.iscancelled(): sleep(0.001)

这对我来说非常有效。用于挂起连接或正在进行的传输。 可能仍然存在一些警告或错误,最好说是错误,需要修复。

【讨论】:

  • 我不相信这个!提供一个根本无法解决问题的答案的人获得了投票。我提供了一些有意义的东西并且被否决了。人有时候真的很奇怪。我认为一个问题已经足够清楚了。您需要在 HTTP 请求中间关闭一个程序。我做对了吗?所以你必须断开连接。对吗?
  • 我实际上对你的答案投了赞成票,对另一个答案投了反对票,但我的投票还没有改变分数,对不起。
  • 无论如何,谢谢,您的答案正是我想要的,但它似乎不起作用。我想我会把请求放在一个进程上,然后我会杀死它。
  • 我写了一个更广泛的例子。这个对我有用。当一个大文件正在下载然后我取消它,或者当你等待来自服务器的数据时。在 IO 中间杀死一个进程或线程并不是真正应该做的事情。
  • @Jerther : urlopen() 是非阻塞的。它会返回一个可以理解 HTTP 协议的类文件对象。问题是,我们在传输或保持连接的过程中关闭了套接字,这会产生错误,当然,我们忽略了这一点,因为预期的效果是中断传输。数据的获取是真正在线程中运行以允许我们这样做的数据。
【解决方案2】:

您可以使用简单的 try-except 块和requests

try:
    r = requests.get(url, timeout=5)
except requests.exceptions.RequestException as e:
    print(e)
    continue

编辑添加超时参数。

【讨论】:

  • 这如何解决需要停止长时间运行但没有返回的请求的问题?
  • 对不起,可能我没有解释清楚。当我提出请求时,它会挂起,例如 30 秒。在这 30 秒之后,它返回一个空响应。我想告诉 requests(或 urllib3 或其他东西)放弃等待响应。
  • 您可以传递requests 一个超时值,如下所示:requests.get('http://github.com', timeout=0.001)。请参阅此处的文档:docs.python-requests.org/en/latest/user/quickstart/#timeouts
  • 这并不能解决问题,它只会挂起,直到 5 秒结束然后超时。此答案无法停止请求。 try except 块什么也不做。有关实际解决方案,请参阅 Dalen 的答案
猜你喜欢
  • 2013-06-30
  • 1970-01-01
  • 1970-01-01
  • 2020-08-19
  • 2015-10-28
  • 1970-01-01
  • 1970-01-01
  • 2018-12-17
  • 1970-01-01
相关资源
最近更新 更多