likes
comments
collection
share

三分钟了解:从 uvicorn 异步 了解 Web 应用程序的 Python 协议 ASGI我们再写 FastAPI 的

作者站长头像
站长
· 阅读数 27

我们再写 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应用程序是一个可调用的对象,通常是一个异步函数,它接受两个参数:scopereceive,并返回一个send异步生成器。

  • scope: 一个字典,包含了关于请求的信息,如请求类型(HTTP或WebSocket)、路径、查询字符串、服务器信息等。
  • receive: 一个异步调用,用于接收从ASGI服务器传来的事件。
  • send: 一个异步生成器,用于将事件发送回ASGI服务器。

服务器接口

服务器接口的主要职责包括:

  • 接受来自客户端的连接。
  • 为每个连接创建一个scope
  • 调用应用程序的receivesend方法来传递事件。
  • 处理网络异常和关闭连接。

事件循环接口

事件循环接口是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']: lifespan
  • message['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
评论
请登录