读源码 - uvicorn 篇 - 1 - 准备

开篇

上一个系列我们过了一遍 gunicorn 的源码,这次我们来看另一个类似而又不同的项目 uvicorn

根据官网介绍,uvicorn 是一个基于 asyncioASGI 服务器。我们上次讨论 gunicorn 的源码时,我们知道它是一个 WSGI 的服务器,那么这次的 ASGI 协议又是什么,它与 WSGI 协议有什么区别呢?

ASGI 协议

ASGI 协议是一种用于处理异步请求和响应的协议。

背景与诞生原因

ASGI(Asynchronous Server Gateway Interface) 诞生于2019年,是 WSGI 协议的异步扩展。随着 Python3.5+async/await 语法的完善,传统 WSGI 同步模型无法满足以下需求:

  • 实时通信(WebSocketMQTT
  • 长连接处理(HTTP/2 Server Push
  • 高并发 I/O 密集型场景
  • 异步框架(FastAPIQuart)的发展需求

继而提出了 ASGI 协议。

WSGI 对比

WSGI ASGI
协议类型 同步阻塞 异步非阻塞
支持协议 HTTP 1.1 HTTP/1.1, HTTP/2, WebSocket
请求处理模型 单线程同步 事件循环+协程
典型应用 Django, Flask FastAPI, Quart
并发能力 依赖多线程/进程 单线程异步处理
中间件架构 线性中间件链 通道(Channel)机制

这里先做一个简单的了解,后续协议的部分我们会详细讨论。

本节说明

gunicorn 系列一样,本节主要做一些准备工作,包括 python 环境配置,源码获取,源码文件介绍和调试环境准备,在本节结束时,我们应该可以搭建好调试环境,并将服务跑起来。

准备工作

1. 配置python环境

首先我们创建工作目录:

1
mkdir explorer_uvicorn && cd explorer_uvicorn

创建虚拟环境:

1
python -m venv env

加载虚拟环境:

1
source env/bin/activate

检查虚拟环境是否成功加载:

1
which python

如果输出像我的这样就是 OK 了:

1
/Users/chundi/explorer_uvicorn/env/bin/python

2. 获取源码

uvicorn 官网找到它的 github 仓库,复制 git clone 的地址,将源码 clone 到本地:

1
git clone https://github.com/encode/uvicorn.git

检出最新的版本(写这篇文章时最新的版本是 0.34.3):

1
cd uvicorn && git checkout -b 0.34.3 0.34.3

3. 用 vscode 打开 explorer_uvicorn 目录

如果没有安装插件的话参考 安装 vscode-python 插件

准备工作就完成了,开搞!

文件说明

老样子,我们先看下都有哪些文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
uvicorn
├── docs 文档资源目录
├── pyproject.toml 项目配置文件(PEP 518标准)
├── requirements.txt Python依赖包列表
├── scripts 可执行脚本目录
├── tests 单元测试套件
├── uvicorn
│ ├── __init__.py 包初始化文件
│ ├── __main__.py 命令行入口模块
│ ├── _subprocess.py 子进程管理工具
│ ├── _types.py 类型定义模块
│ ├── config.py 配置管理组件
│ ├── importer.py 动态导入工具
│ ├── lifespan 生命周期事件处理
│ ├── logging.py 日志系统实现
│ ├── loops 事件循环适配器
│ ├── main.py 核心应用逻辑
│ ├── middleware 中间件抽象层
│ ├── protocols 网络协议实现
│ │ ├── __init__.py 协议基础模块
│ │ ├── http HTTP协议栈
│ │ ├── utils.py 协议工具函数
│ │ └── websockets WebSocket协议支持
│ ├── py.typed 类型检查标记文件
│ ├── server.py 服务器核心实现
│ ├── supervisors 进程监控模块
│ ├── workers.py 工作进程管理
│ └── ...... 其他未显示模块
└── ...... 其他未显示根目录文件

调试准备

1. 加载虚拟环境

参考上次 gunicorn 的选择 virtualenv在 vscode 中选择 virtualenv,这里就不赘述了。

2. 使用开发模式安装 uvicorn

进入 explorer_uvicorn 目录,运行:

1
pip install -e uvicorn

3. 创建应用程序文件

explorer_uvicorn 目录下创建 myapp 文件夹,添加 main.py 文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
async def app(scope, receive, send):
assert scope['type'] == 'http'
await send({
'type': 'http.response.start',
'status': 200,
'headers': [
(b'content-type', b'text/plain'),
(b'content-length', b'13'),
],
})
await send({
'type': 'http.response.body',
'body': b'Hello, world!',
})

4. 创建调试配置文件

参考 gunicorn创建调试配置文件,添加如下配置并保存:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"version": "0.2.0",
"configurations": [
{
"name": "uvicorn",
"type": "debugpy",
"request": "launch",
"module": "uvicorn",
"args": [
"main:app",
"--loop",
"asyncio"
],
"console": "integratedTerminal",
"cwd": "${workspaceFolder}/myapp",
"justMyCode": false
}
]
}

5. 调试运行

点击调试按钮,在 vscode 的终端应该就能看到类似下边的输出:

1
2
3
4
5
INFO:     Started server process [22266]
INFO: Waiting for application startup.
INFO: ASGI 'lifespan' protocol appears unsupported.
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

准备工作就完成了!

打开浏览器,访问 http://127.0.0.1:8000

Hello World! Again!