阅读(4517) (0)

Tornado 主事件循环

2022-03-10 09:09:29 更新

用于非阻塞套接字的 I/O 事件循环。

在 Tornado 6.0 中,​IOLoop是 ​asyncio事件循环的包装器,由于历史原因,接口略有不同。 应用程序可以直接使用 ​IOLoop接口或底层​asyncio​事件循环(除非需要与旧版本的 Tornado 兼容,在这种情况下必须使用 ​IOLoop​)。

典型的应用程序将使用单个 ​IOLoop对象,通过IOLoop.current类方法访问。IOLoop.start方法(或等效的 ​asyncio.AbstractEventLoop.run_forever​)通常应在 ​main()​ 函数的末尾调用。 非典型应用程序可能使用多个 ​IOLoop​,例如每个线程或每个单元测试用例一个 ​IOLoop​。

IOLoop 对象

class tornado.ioloop.IOLoop

一个 I/O 事件循环。

从 Tornado 6.0 开始,​IOLoop是 ​asyncio事件循环的包装器。

简单 TCP 服务器的示例用法:

import errno
import functools
import socket

import tornado.ioloop
from tornado.iostream import IOStream

async def handle_connection(connection, address):
    stream = IOStream(connection)
    message = await stream.read_until_close()
    print("message from client:", message.decode().strip())

def connection_ready(sock, fd, events):
    while True:
        try:
            connection, address = sock.accept()
        except BlockingIOError:
            return
        connection.setblocking(0)
        io_loop = tornado.ioloop.IOLoop.current()
        io_loop.spawn_callback(handle_connection, connection, address)

if __name__ == '__main__':
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.setblocking(0)
    sock.bind(("", 8888))
    sock.listen(128)

    io_loop = tornado.ioloop.IOLoop.current()
    callback = functools.partial(connection_ready, sock)
    io_loop.add_handler(sock.fileno(), callback, io_loop.READ)
    io_loop.start()

默认情况下,新构建的 ​IOLoop成为线程的当前 ​IOLoop​,除非已经存在当前 ​IOLoop​。 此行为可以通过 ​IOLoop构造函数的make_current参数来控制:如果 ​make_current=True​,新的 ​IOLoop将始终尝试成为当前实例,如果已经存在当前实例,则会引发错误。 如果 ​make_current=False​,新的 ​IOLoop将不会尝试成为当前的。

一般来说,​IOLoop不能在分叉中存活或以任何方式跨进程共享。 当使用多个进程时,每个进程都应该创建自己的 ​IOLoop​,这也意味着任何依赖于 ​IOLoop的对象(例如 ​AsyncHTTPClient​)也必须在子进程中创建。 作为指导,任何启动进程(包括 ​tornado.process​ 和 ​multiprocessing模块)都应该尽早启动,最好是应用程序在 ​main()​ 中加载其配置后首先执行的操作。

在 4.2 版更改: 向 ​IOLoop构造函数添加了 ​make_current关键字参数。

在 5.0 版更改: 默认使用 ​asyncio事件循环。 ​IOLoop.configure​ 方法不能在 Python 3 上使用,除非冗余指定 ​asyncio事件循环。

运行IOLoop

static IOLoop.current(instance: bool = True) → Optional[tornado.ioloop.IOLoop]

返回当前线程的 ​IOLoop​。

如果 ​IOLoop当前正在运行或已被 ​make_current标记为当前,则返回​instance​。 如果没有当前 ​IOLoop并且​instance​为真,则创建一个。

在 4.1 版更改: 添加了实例参数来控制对 ​IOLoop.instance()​ 的回退。

在 5.0 版更改:在 Python 3 上,当前 ​IOLoop的控制权委托给 ​asyncio​,并使用此方法和其他方法作为传递访问器。 ​instance参数现在控制在没有 ​IOLoop时是否自动创建,而不是我们是否回退到 ​IOLoop.instance()​ (现在是此方法的别名)。 ​instance=False​ 已弃用,因为即使我们不创建 ​IOLoop​,此方法也可能会初始化 ​asyncio循环。

IOLoop.make_current() → None

使其成为当前线程的 ​IOLoop​。

IOLoop在启动时自动成为其线程的当前线程,但有时在启动 ​IOLoop之前显式调用 ​make_current很有用,以便在启动时运行的代码可以找到正确的实例。

在 4.1 版更改: 在没有当前 ​IOLoop时创建的 ​IOLoop将自动变为当前。

在 5.0 版更改: 此方法还设置当前的 ​asyncio事件循环。

static IOLoop.clear_current() → None

清除当前线程的 ​IOLoop​。

主要供测试框架在测试之间使用。

在 5.0 版更改: 此方法还清除当前的 ​asyncio事件循环。

IOLoop.start() → None

启动​I/O​ 循环。

循环将一直运行,直到其中一个回调调用 ​stop()​,这将使循环在当前事件迭代完成后停止。

IOLoop.stop() → None

停止 ​I/O​ 循环。

如果事件循环当前未运行,则对 ​start()​ 的下一次调用将立即返回。

请注意,即使在调用 ​stop ​之后,​IOLoop也不会完全停止,直到 ​IOLoop.start​ 也返回。 在调用停止之前安排的一些工作可能在 ​IOLoop​ 关闭之前仍然运行。

IOLoop.run_sync(func: Callable, timeout: Optional[float] = None) → Any

启动 ​IOLoop​,运行给定函数,然后停止循环。

该函数必须返回一个可等待对象或无。 如果函数返回一个可等待对象,​IOLoop​ 将一直运行,直到解决了可等待对象(并且 ​run_sync()​ 将返回可等待对象的结果)。 如果引发异常,​IOLoop将停止,异常将重新引发给调用者。

仅关键字参数 ​timeout​ 可用于设置函数的最大持续时间。 如果超时到期,则会引发 ​tornado.util.TimeoutError​。

此方法对于允许在 ​main()​ 函数中进行异步调用很有用:

async def main():
    # do stuff...

if __name__ == '__main__':
    IOLoop.current().run_sync(main)

IOLoop.close(all_fds: bool = False) → None

关闭 ​IOLoop​,释放所有使用的资源。

如果 ​all_fds为真,则在 ​IOLoop上注册的所有文件描述符都将被关闭(不仅仅是 ​IOLoop本身创建的文件描述符)。

许多应用程序只会使用一个在整个进程生命周期内运行的 ​IOLoop​。 在这种情况下,不需要关闭 ​IOLoop​,因为当进程退出时,所有内容都会被清理。 ​IOLoop.close​ 主要用于单元测试等场景,创建和销毁大量的​IOLoop​。

IOLoop必须完全停止才能关闭。 这意味着在尝试调用 ​IOLoop.close()​ 之前必须调用 ​IOLoop.stop()​ 并且必须允许 ​IOLoop.start()​ 返回。 因此,​close调用通常会出现在 ​start调用之后,而不是在 ​stop调用附近。

在 3.1 版更改:如果 ​IOLoop实现支持“文件描述符”的非整数对象,则当 ​all_fds为 ​true时,这些对象将具有其 ​close​ 方法。

static IOLoop.instance() → tornado.ioloop.IOLoop

IOLoop.current()​ 的已弃用别名。

在 5.0 版更改: 以前,此方法返回一个全局单例 ​IOLoop​,与 ​current()​ 返回的每个线程 ​IOLoop​ 形成对比。 在几乎所有情况下,两者都是相同的(当它们不同时,它通常用于非 Tornado 线程与主线程的 ​IOLoop ​进行通信)。 这种区别在 ​asyncio中不存在,因此为了便于与该包集成,​instance()​ 已更改为 ​current()​ 的别名。 使用 ​instance()​ 的跨线程通信方面的应用程序应该将自己的全局变量设置为指向他们想要使用的 ​IOLoop​。

自 5.0 版起已弃用。

IOLoop.install() → None

不推荐使用 ​make_current()​ 的别名。

在 5.0 版更改: 以前,此方法将此 ​IOLoop设置为​IOLoop.instance()使用的全局单例。 现在 ​instance()​ 是 ​current()​ 的别名,​install()​ 是 ​make_current()​ 的别名。

自 5.0 版起已弃用。

static IOLoop.clear_instance() → None

不推荐使用 ​clear_current()​ 的别名。

在 5.0 版更改: 以前,此方法将清除 ​IOLoop.instance()​ 用作全局单例的 ​IOLoop​。 现在 ​instance()​ 是 ​current()​ 的别名,​clear_instance()​ 是 ​clear_current()​ 的别名。

自 5.0 版起已弃用。

I/O事件

IOLoop.add_handler(fd: Union[int, tornado.ioloop._Selectable], handler: Callable[[...], None], events: int) → None

注册给定的处理程序以接收fd的给定事件。

fd​参数可以是整数文件描述符,也可以是具有 ​fileno()​ 和 ​close()​ 方法的类文件对象。

events参数是常量 ​IOLoop.READ​、​IOLoop.WRITE​ 和 ​IOLoop.ERROR​ 的按位或。

当事件发生时,将运行 ​handler(fd, events)​。

在 4.0 版更改: 除了原始文件描述符之外,还添加了传递类似文件的对象的能力。

IOLoop.update_handler(fd: Union[int, tornado.ioloop._Selectable], events: int) → None

改变我们监听​fd​上的事件。

在 4.0 版更改: 除了原始文件描述符之外,还添加了传递类似文件的对象的能力。

IOLoop.remove_handler(fd: Union[int, tornado.ioloop._Selectable]) → None

停止监听fd上的事件。

在 4.0 版更改: 除了原始文件描述符之外,还添加了传递类似文件的对象的能力。

回调和超时

IOLoop.add_callback(callback: Callable, *args, **kwargs) → None

在下一次 ​I/O​ 循环迭代中调用给定的回调。

任何时候从任何线程调用此方法都是安全的,信号处理程序除外。 请注意,这是 ​IOLoop中唯一保证线程安全的方法; 与 ​IOLoop的所有其他交互都必须从该 ​IOLoop的线程中完成。 ​add_callback()​ 可用于控制从其他线程转移到 ​IOLoop的线程。

IOLoop.add_callback_from_signal(callback: Callable, *args, **kwargs) → None

在下一次 ​I/O​ 循环迭代中调用给定的回调。

可从 Python 信号处理程序安全使用; 否则不应使用。

IOLoop.add_future(future: Union[Future[_T], concurrent.futures.Future[_T]], callback: Callable[[Future[_T]], None]) → None

当给定的 ​Future完成时,在 ​IOLoop​ 上安排回调。

使用一个参数 ​Future调用回调。

此方法仅接受 ​Future对象而不接受其他可等待对象(与大多数 Tornado 不同,两者可互换)。

IOLoop.add_timeout(deadline: Union[float, datetime.timedelta], callback: Callable[[...], None], *args, **kwargs) → object

在 ​I/O​ 循环的时间截止日期运行回调。

返回一个不透明的句柄,可以传递给 ​remove_timeout以取消。

截止日期可以是一个表示时间的数字(与 ​IOLoop.time​ 的比例相同,通常是 ​time.time​),或者是相对于当前时间的截止日期的 ​datetime.timedelta​ 对象。 从 Tornado 4.0 开始, ​call_later是相对情况下更方便的替代方案,因为它不需要 ​timedelta对象。

请注意,从其他线程调用 ​add_timeout是不安全的。 相反,您必须使用 ​add_callback将控制权转移到 ​IOLoop的线程,然后从那里调用 ​add_timeout

IOLoop的子类必须实现 ​add_timeout或 ​call_at​; 每个的默认实现将调用另一个。 ​call_at通常更容易实现,但是希望与 Tornado 4.0 之前的版本保持兼容性的子类必须使用 ​add_timeout代替。

在 4.0 版更改: 现在通过 *args 和 **kwargs 回调。

IOLoop.call_at(when: float, callback: Callable[[...], None], *args, **kwargs) → object

在 ​when指定的绝对时间运行​callback​。

when必须是使用与 ​IOLoop.time​ 相同的参考点的数字。

返回一个不透明的句柄,可以传递给 ​remove_timeout以取消。 注意,与同名的asyncio方法不同,返回的对象没有 ​cancel()​ 方法。

IOLoop.call_later(delay: float, callback: Callable[[...], None], *args, **kwargs) → object

在​delay​过去后运行​callback​。

返回一个不透明的句柄,可以传递给 ​remove_timeout以取消。 注意,与同名的 ​asyncio方法不同,返回的对象没有 ​cancel()​ 方法。

IOLoop.remove_timeout(timeout: object) → None

取消挂起的超时。

参数是 ​add_timeout返回的句柄。 即使回调已经运行,调用 ​remove_timeout也是安全的。

IOLoop.spawn_callback(callback: Callable, *args, **kwargs) → None

在下一次 ​IOLoop迭代中调用给定的回调。

从 Tornado 6.0 开始,此方法等效于 ​add_callback​。

IOLoop.run_in_executor(executor: Optional[concurrent.futures._base.Executor], func: Callable[[...], _T], *args) → Awaitable[_T]

在 ​concurrent.futures.Executor​ 中运行一个函数。 如果 ​executor为 ​None​,将使用 IOLoop默认的 ​executor​。

使用 ​functools.partial​ 将关键字参数传递给 ​func​。

IOLoop.set_default_executor(executor: concurrent.futures._base.Executor) → None

设置与 ​run_in_executor()​ 一起使用的默认执行程序。

IOLoop.time() → float

根据 ​IOLoop的时钟返回当前时间。

返回值是相对于过去未指定时间的浮点数。

IOLoop可以定制为使用例如 ​time.monotonic​ 代替 ​time.time​,但目前不支持此方法,因此此方法等效于 ​time.time

class tornado.ioloop.PeriodicCallback(callback: Callable[[], None], callback_time: float, jitter: float = 0)。

安排定期调用给定的回调。

每个​callback_time毫秒调用一次回调。 请注意,超时以毫秒为单位,而 Tornado 中大多数其他与时间相关的函数使用秒。

如果指定了​jitter​,每个回调时间将在一个​jitter * callback_time​毫秒的窗口内随机选择。 抖动可用于减少具有相似周期的事件的对齐。 0.1 的抖动意味着允许 10% 的回调时间变化。 窗口以 ​callback_time​ 为中心,因此给定时间间隔内的调用总数不应受到添加抖动的显着影响。

如果回调运行时间超过 ​callback_time​ 毫秒,则将跳过后续调用以按计划返回。

start必须在 ​PeriodicCallback​ 创建后调用。

在 5.0 版中更改: ​io_loop参数(自 4.1 版以来已弃用)已被删除。

在 5.1 版更改: 添加了 ​jitter参数。

start() → None

启动计时器。

stop() → None

停止计时器。

is_running() → bool

如果此 ​PeriodicCallback已启动,则返回 ​True​。