三分钟了解:从 uvicorn 异步 了解 Web 应用程序的 Python 协议 ASGI我们再写 FastAPI 的
我们再写 FastAPI 的时候需要使用 uvicorn 服务器。初学者会问为什么呢? 今天我们就来解答这个问题。
uvicorn
我们使用 http 实现一个简单的 http 请求:
import json
# 定义一个辅助函数,将 headers 中的 bytes 转换为字符串
def convert_bytes_to_str(data):
if isinstance(data, bytes):
return data.decode('utf-8')
if isinstance(data, tuple):
return tuple(convert_bytes_to_str(item) for item in data)
if isinstance(data, list):
return [convert_bytes_to_str(item) for item in data]
if isinstance(data, dict):
return {key: convert_bytes_to_str(value) for key, value in data.items()}
return data
async def app(scope, receive, send):
# print(scope)
data = convert_bytes_to_str(scope)
print(json.dumps(data, indent=4))
# 检查请求的类型
if scope['type'] == 'http':
# 等待 HTTP 请求体
event = await receive()
# 响应内容
response_body = json.dumps({"message": "Hello, ASGI!"}).encode('utf-8')
# 发送 HTTP 响应头
await send({
'type': 'http.response.start',
'status': 200,
'headers': [
(b'content-type', b'application/json'),
],
})
# 发送 HTTP 响应体
await send({
'type': 'http.response.body',
'body': response_body,
})
scope 字段中带有二进制的字符串,我们需要一个抓换函数 convert_bytes_to_str.我们简单分析一下,请求路径方法相关的都在 scope 中, scope 不仅仅支持 http 所以需要使用 type 判断当前协议的类型。然后使用 receive 函数接收请求体。然后使用 json.dumps 做一个响应体,并使用 encode 进行编码。最后发送请求头和请求体。
这里发送了两次,这设计符合异步的需求,增强不同的应用场景的处理能力(流式响应)。有了实际项目认知我们就可以从理论基础了解 ASGI 了。
ASGI
异步服务器网关接口(Asynchronous Server Gateway Interface),是一个用于构建异步 Web 应用程序的 Python 协议。
ASGI主要特点
ASGI 的主要特点:
- 支持异步
- 支持多协议:支持 websocket 和长轮询。也就一意味着, ASGI 支持多协议:
http/https/websocket
。 - 并发支持
- 通信:应用程序和服务器之间,以及应用程序的不同部分之间通过发送和接收消息进行交互。
ASGI协议组成
应用程序接口
ASGI应用程序接口定义了应用程序如何与ASGI服务器交互。一个ASGI应用程序是一个可调用的对象,通常是一个异步函数,它接受两个参数:scope
和receive
,并返回一个send
异步生成器。
scope
: 一个字典,包含了关于请求的信息,如请求类型(HTTP或WebSocket)、路径、查询字符串、服务器信息等。receive
: 一个异步调用,用于接收从ASGI服务器传来的事件。send
: 一个异步生成器,用于将事件发送回ASGI服务器。
服务器接口
服务器接口的主要职责包括:
- 接受来自客户端的连接。
- 为每个连接创建一个
scope
。 - 调用应用程序的
receive
和send
方法来传递事件。 - 处理网络异常和关闭连接。
事件循环接口
事件循环接口是ASGI协议中隐含的一部分,它是由ASGI服务器管理的,而不是直接由ASGI协议定义。
事件循环负责调度和执行异步任务,是异步编程的核心。事件循环的主要功能包括:
- 运行和调度异步任务。
- 管理异步I/O操作,如网络请求。
- 处理回调函数和异步生成器。
在ASGI中,事件循环通常由以下Python库提供:
- asyncio: Python标准库中的异步I/O框架。
- uvloop: 一个基于libuv的异步事件循环,通常与Uvicorn服务器一起使用。
ASGI服务器和应用程序都依赖于事件循环来执行异步操作,这使得它们能够高效地处理大量并发连接。
ASGI 事件
ASGI 事件驱动模型处理生命周期管理(启动和关闭)、HTTP 请求的处理、WebSocket 连接的管理等。通过不同类型的事件,开发者可以精细地控制连接和数据流动,实现异步并发处理。
Lifespan 事件
lifespan
事件与 ASGI 应用的启动和关闭周期相关,它通常用于执行初始化和清理任务。
- lifespan.startup
- lifespan.shutdown
HTTP 事件
ASGI 对 HTTP 请求的处理分为多个事件,允许对每个 HTTP 请求的细节进行管理.
- http.request
- http.response.start
- http.response.body
- http.disconnect
- websocket.send
WebSocket 事件
ASGI 支持 WebSocket 连接,允许双向通信。
- websocket.connect
- websocket.receive
ASGI生命周期
ASGI 应用生命周期是指应用本身在启动和关闭过程中经历的阶段。它由 lifespan
事件通道管理,
scope['type']
: lifespanmessage['type']
: 消息启动关闭:lifespan.startup/lifespan.shutdown
- 发送类型:
{'type': 'lifespan.shutdown.complete'}
ASGI 的生命周期分为两部分:应用的启动和关闭,以及请求的建立、处理、响应和关闭。
uvicron 与应用层
前面已经提到了 uvicron 实现了 ASGI 服务器层。但是它不好用,太底层了,实现应用层的不好用,于是又了很多的上层框架出现:
- Starlette:是一个轻量级的 ASGI 框架/工具包,用于构建高性能的 Web 服务。它是 FastAPI 的一个基础组件,也可以独立使用来创建简单的 Web 应用。
- FastAPI:是一个现代、快速(高性能)的 Web 框架,用于构建 API,与 Python 3.6+ 类型提示一起使用。它于Starlette 和 标准 Python 类型提示,并且提供了自动的数据验证和序列化,以及交互式的 API 文档生成。
这里其他的就不用多说了,贪多也嚼不烂。uvicron 需要一个 app,所有这两个框架,示例化都是 app
。然后使用 uvicorn 启动cli 启动:
uvicorn main:app --reload
小结
本文主要从 uvicorn 中了解 Python web ASGI 协议。ASGI 是 Python 的异步服务器网关接口,集成现代 Python web 程序的异步,并发以及多协议的实现。
转载自:https://juejin.cn/post/7423698585970049059