跳转到主要内容

ASGI规范、辅助代码和适配器

项目描述

https://github.com/django/asgiref/actions/workflows/tests.yml/badge.svg https://img.shields.io/pypi/v/asgiref.svg

ASGI是Python异步Web应用和服务器之间通信的标准,定位为WSGI的异步继任者。您可以在此了解更多信息:https://asgi.readthedocs.io/en/latest/

此包包括ASGI基础库,例如

  • 同步到异步和异步到同步函数包装,asgiref.sync

  • 服务器基类,asgiref.server

  • ASGI到WSGI的适配器,位于asgiref.wsgi

函数包装器

这些允许您包装或装饰异步或同步函数,以便可以从另一种样式调用它们(因此您可以从同步线程调用异步函数,反之亦然)。

特别是

  • AsyncToSync允许同步子线程在主线程的事件循环上调用异步函数时停止并等待,然后在异步函数完成时将控制权返回给线程。

  • SyncToAsync允许异步代码调用同步函数,该函数在线程池中运行,并在同步函数完成时将控制权返回给异步协程。

目的是使从异步代码调用同步API和从同步代码调用异步API变得更容易,以便更容易从一种样式过渡到另一种样式。在Channels的情况下,我们使用SyncToAsync包装(同步的)Django视图系统,以便它在(异步的)ASGI服务器中运行。

请注意,事物运行在哪些线程中是非常具体的,旨在与旧同步代码保持最大兼容性。有关完整说明,请参阅下文中的“同步代码 & 线程”。默认情况下,sync_to_async将出于安全原因在程序中的同一线程中运行所有同步代码;您可以通过使用@sync_to_async(thread_sensitive=False)来禁用此功能以获得更高的性能,但请确保在执行时您的代码不依赖于绑定到线程的任何内容(如数据库连接)。

Threadlocal替换

这是一个可以用于线程和asyncio任务的Threadlocal替换。更好的是,当您使用sync_to_async在线程池中运行事物时,它将代理从任务局部上下文到线程局部上下文的值,反之亦然。

如果您想要真正的线程和任务安全性,您可以在Local对象上设置thread_critical以确保这一点。

服务器基类

包括一个StatelessServer类,它提供了编写无状态服务器(即不处理直接传入的套接字,而是消费外部流或套接字以确定正在发生的事情)的所有困难工作。

这样的服务器的一个例子是聊天机器人服务器,该服务器连接到中央聊天服务器并为与它聊天的每个用户提供一个“连接作用域”。只有一个实际连接,但服务器必须将事物分开成几个作用域,以便更容易编写代码。

您可以在frequensgi中看到如何使用此示例。

WSGI-to-ASGI适配器

允许您包装一个WSGI应用程序,使其看起来像一个有效的ASGI应用程序。

只需像这样将其包装在您的WSGI应用程序周围

asgi_application = WsgiToAsgi(wsgi_application)

WSGI应用程序将在同步线程池中运行,包装的ASGI应用程序将是一个接受http类消息的应用程序。

请注意,WSGI的所有扩展功能可能不支持(例如,用于传入POST主体的文件句柄)。

依赖关系

asgiref需要Python 3.8或更高版本。

贡献

请参阅主要Channels贡献文档

测试

要运行测试,请确保您已使用与包一起安装的tests额外功能。

cd asgiref/
pip install -e .[tests]
pytest

构建文档

文档使用Sphinx

cd asgiref/docs/
pip install sphinx

要构建文档,您可以使用默认工具

sphinx-build -b html . _build/html  # or `make html`, if you've got make set up
cd _build/html
python -m http.server

…或者您可以使用sphinx-autobuild来运行服务器并自动重建/重新加载您的文档更改

pip install sphinx-autobuild
sphinx-autobuild . _build/html

发布

要发布,首先在CHANGELOG.txt中添加详细信息,并更新asgiref/__init__.py中的版本号。

然后,构建并推送包

python -m build
twine upload dist/*
rm -r build/ dist/

实现细节

同步代码 & 线程

《asgiref.sync》模块提供了两个包装器,允许您在异步和同步代码之间随意切换,同时为您处理粗糙的边缘。

不幸的是,粗糙的边缘很多,代码需要特别努力才能尽可能地在同一个线程中保持事物的同步。值得注意的是,我们正在处理以下限制

  • 通过《SyncToAsync》调用并标记为《thread_sensitive》的所有同步代码应在同一线程中运行(如果程序的外层是同步的,则为主线程)

  • 如果线程已经有一个正在运行的异步循环,那么如果它在调用栈中阻塞在您之上的同步代码上,则《AsyncToSync》无法在该循环上运行事物。

您可能得到的第一个妥协可能是,《thread_sensitive》代码应仅在同一个线程中运行,而不是在子线程中启动,以满足第一个限制,但这立即导致第二个限制。

唯一的真正解决方案是在ThreadPoolExecutor的一个变体中执行任何《thread_sensitive》代码,使其在最外层的同步线程上运行 - 要么是主线程,要么是单个生成的子线程。

这意味着您现在有两个基本状态

  • 如果程序的外层是同步的,那么通过《AsyncToSync》运行的所有异步代码将在任意子线程中的每个调用事件循环中运行,而所有《thread_sensitive》代码将在主线程中运行。

  • 如果程序的外层是异步的,那么所有异步代码都在主线程的事件循环上运行,而所有《thread_sensitive》同步代码都在单个共享子线程中运行。

关键的是,这意味着在两种情况下,都有一个线程是共享资源,所有《thread_sensitive》代码都必须在该线程上运行,而且有可能这个线程目前正在阻塞在其自己的《AsyncToSync》调用上。因此,《AsyncToSync》需要在其阻塞期间充当线程代码的执行器。

《CurrentThreadExecutor》类提供了这个功能;您不仅可以等待Future,还可以调用它的《run_until_future》方法,它将运行提交的代码直到该Future完成。这意味着可以在调用内部运行代码在您的线程上。

维护和安全

要报告安全问题,请联系 security@djangoproject.com。有关GPG签名和更多信息,请参阅 https://docs.django.ac.cn/en/dev/internals/security/

要报告错误或请求新功能,请打开一个新的GitHub问题。

此存储库是Channels项目的一部分。对于牧羊人和维护团队,请参阅 主Channels说明文件

项目详情


发布历史 发布通知 | RSS源

下载文件

下载适合您平台的文件。如果您不确定选择哪个,请了解有关安装包的更多信息。

源分布

asgiref-3.8.1.tar.gz (35.2 kB 查看哈希值)

上传时间

构建分布

asgiref-3.8.1-py3-none-any.whl (23.8 kB 查看哈希值)

上传时间 Python 3

支持者