【问题标题】:How can I add a connection timeout with asyncio?如何使用 asyncio 添加连接超时?
【发布时间】:2015-06-27 16:29:54
【问题描述】:

我想快速连接到许多不同站点的列表。 我使用 asyncio 以异步方式执行此操作,现在想要 如果连接响应时间过长,则应在连接被忽略时添加超时。

我该如何实现?

import ssl
import asyncio
from contextlib import suppress
from concurrent.futures import ThreadPoolExecutor
import time


@asyncio.coroutine
def run():
    while True:
        host = yield from q.get()
        if not host:
            break

        with suppress(ssl.CertificateError):
            reader, writer = yield from asyncio.open_connection(host[1], 443, ssl=True) #timout option?
            reader.close()
            writer.close()


@asyncio.coroutine
def load_q():
    # only 3 entries for debugging reasons
    for host in [[1, 'python.org'], [2, 'qq.com'], [3, 'google.com']]:
        yield from q.put(host)
    for _ in range(NUM):
        q.put(None)


if __name__ == "__main__":
    NUM = 1000
    q = asyncio.Queue()

    loop = asyncio.get_event_loop()
    loop.set_default_executor(ThreadPoolExecutor(NUM))

    start = time.time()
    coros = [asyncio.async(run()) for i in range(NUM)]
    loop.run_until_complete(load_q())
    loop.run_until_complete(asyncio.wait(coros))
    end = time.time()
    print(end-start)

(旁注:有人知道如何优化这个吗?)

【问题讨论】:

  • 您忘记了在load_q 中对q.put(None) 的调用yield from,因此此代码将无法按照当前编写的方式运行。
  • 这里不需要读者,作者。您可以将asyncio.create_connectionProtocol 一起使用,它什么都不做(它会在网络连接建立后立即关闭)。这是code example that I've tried on top million Alexa site list(它可能有点过时,例如,它不使用一些便利功能,例如asyncio.wait_for())。它使用单线程并最多打开limit ssl 连接。

标签: python python-3.x asynchronous timeout python-asyncio


【解决方案1】:

您可以将对open_connection 的调用包装在asyncio.wait_for 中,这样您就可以指定超时时间:

    with suppress(ssl.CertificateError):
        fut = asyncio.open_connection(host[1], 443, ssl=True)
        try:
            # Wait for 3 seconds, then raise TimeoutError
            reader, writer = yield from asyncio.wait_for(fut, timeout=3)
        except asyncio.TimeoutError:
            print("Timeout, skipping {}".format(host[1]))
            continue

请注意,当TimeoutError 被引发时,open_connection 协程也会被取消。如果你不希望它被取消(尽管我认为你确实希望在这种情况下它被取消),你可以在asyncio.shield 中封装调用。

【讨论】:

  • 但这也会使其成为阻塞调用,不是吗?就像一个接一个地在正常循环中打开连接。
  • @ali 否,因为对run 方法的所有调用都包含在asyncio.async 调用中,这意味着它们都同时运行。
  • 如果连接超时需要在另一个协程内,参见[stackoverflow.com/questions/28609534/…asyncio force timeout)关于堆叠asyncio.ensure_future(asyncio.wait_for(create_connection()))
  • 我很确定这停止使用 3.7,因为 wait_for 文档中提到的这个变化-Changed in version 3.7: When aw is cancelled due to a timeout, wait_for waits for aw to be cancelled. Previously, it raised asyncio.TimeoutError immediately.
猜你喜欢
  • 2018-01-07
  • 2013-01-14
  • 1970-01-01
  • 2014-10-19
  • 1970-01-01
  • 2013-04-08
  • 2016-05-08
  • 1970-01-01
相关资源
最近更新 更多