Adi

拖拉的根本原因是能力不足!

开篇

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

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

阅读全文 »

本节说明

本节的标题是——协议,有两层意思,一个是用户请求的协议 HTTP,另一个是 gunicorn 解析请求后,与我们的应用交互的 WSGI 协议,本章节将分两个部分分别讨论。

开始前,我们先回顾一下 HTTP 协议。

HTTP 协议

HTTP 请求

一个完整的 HTTP 请求包含四个部分:

  • 请求行:请求方法 + URL + HTTP 版本
  • 请求头:包含各种元数据信息
  • 空行:分隔头部和消息体
  • 请求体:包含要发送的数据(可选)

示例:

1
2
3
4
5
6
GET /hello?a=123 HTTP/1.1
Host: localhost:8000
User-Agent: curl/8.7.1
Accept: */*
Proxy-Connection: Keep-Alive

HTTP 响应

一个完整的 HTTP 响应结构也包含四个部分:

  • 状态行:HTTP 版本 + 状态码 + 状态描述
  • 响应头:服务器返回的元数据信息
  • 空行:分隔响应头和响应体
  • 响应体:实际返回的数据内容(可选)

示例:

1
2
3
4
5
6
7
8
9
10
HTTP/1.1 200 OK
Content-Length: 13
Connection: keep-alive
Content-Type: text/plain
Date: Mon, 26 May 2025 07:19:15 GMT
Keep-Alive: timeout=4
Proxy-Connection: keep-alive
Server: gunicorn

Hello World!

在 HTTP 请求和响应中,除了请求体和响应体部分,其它的可以看作是纯文本的内容。

参照上边的例子,我们来看一下一个用户请求是如何解析交给 WSGI 应用的。

阅读全文 »

本节说明

上一节我们梳理了 gunicorn 是如何处理配置的,这一节我们来看 gunicorn 的工作进程是如何运行的。

分类

gunicorn 默认实现了 sync、eventlet、gevent、tornado、gthread 这几种类型 worker 的实现,含义如下:

名称 说明 异步 实现
sync 默认类型,每个worker同一时间只处理一个请求 否 workers.sync.SyncWorker
eventlet 基于 eventlet 实现,需要 monkey patch 是 workers.geventlet.EventletWorker
gevent 基于 gevent 实现,需要 monkey patch 是 workers.ggevent.GeventWorker
tornado 集成 Tornado 框架的事件循环机制,适合与 Tornado 应用结合使用 是 workers.gtornado.TornadoWorker
gthread 每个 worker 是一个进程,内部开启多个线程同时处理请求 否 workers.gthread.ThreadWorker

我们从最简单的默认的 sync 类型来看 gunicorn 是如何加载一个 worker 的。

阅读全文 »

本节说明

上一节我们梳理了 gunicorn 服务启动的流程,本章节我们来研究一下它的配置是如何处理的。

来源

从官方文档中我们可以得知,gunicorn 会从 5 个地方读取配置数据,按优先级从低到高分别是:

  1. 从环境变量中读取
  2. 从框架特定的配置文件中读取(目前仅支持 Paster 应用)
  3. 从当前工作目录中的 gunicorn.conf.py 文件中读取,或通过命令行参数指定这个文件
  4. 从名称为 GUNICORN_CMD_ARGS 的环境变量中读取
  5. 命令行启动时指定的参数配置

这几个位置读取到的配置优先级依次增加,不同位置配置的相同参数,优先级高的会覆盖优先级低的。

需要注意的是如果像 GUNICORN_CMD_ARGS 和命令行中都指定了配置文件的路径,则只会解析使用命令行中指定的文件,而不是都解析后进行覆盖合并。

了解了配置的来源及优先级,我们来看下 gunicorn 是怎么做的。

阅读全文 »

本节说明

上一节我们已经可以将服务跑起来了,但是服务器是怎么跑起来的?

本章节我们就来跟踪一下 gunicorn 的启动流程,以及它的进程是如何管理的。

入口

我们知道,一个简单的 gunicorn 服务启动命令,可以在命令行直接输入 gunicorn main:app,那么这个 gunicorn 命令是从哪里来的呢?

在 gunicorn/pyproject.toml 文件中,有这样一个配置段,[project.scripts]:

1
2
3
[project.scripts]
# duplicates "python -m gunicorn" handling in __main__.py
gunicorn = "gunicorn.app.wsgiapp:run"

gunicorn = "gunicorn.app.wsgiapp:run",这个配置就是 gunicorn 程序启动的入口,我们在 pip install gunicorn时,会根据这个配置,在 env/bin 目录生成 gunicorn 的启动脚本,里面的内容是这样的:

1
2
3
4
5
6
7
8
#!/Users/chundi/explorer/env/bin/python
# -*- coding: utf-8 -*-
import re
import sys
from gunicorn.app.wsgiapp import run
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(run())

可以看到也是通过 gunicorn.app.wsgiapp:run 启动的,这个 run 方法就是程序的入口。

阅读全文 »

开篇

开始了,一直想写一些关于阅读源码的东西,终于开始了。

这次选了一个比较熟悉的 python 项目 gunicorn,代码量不大,但功能是完整的,容易阅读,也容易整理,作为一个尝试吧。

本节说明

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

准备工作

1. 配置python环境

我使用的是 3.12 版本的 python,推荐使用 linux/mac 系统,windows 环境也推荐使用 wsl,环境配置会更方便一些。

首先我们创建一个文件夹,作为我们的项目目录,后续所有的操作都在这个目录中进行。

1
mkdir explorer && cd explorer
阅读全文 »

博客搭了这么久了,一直想写一些东西,但也一直没有动手开始写,总觉得很多东西看得还不够明白,没有完全弄清楚,写不出什么东西来,各种找借口。。最近越来越觉得,如果一直不写,就永远也写不出来。。

所以现在打算一点一点开始,把一直想写的一个东西写出来,就叫『大家一起读源码』,可能会只有一个项目,也可能会写一个系列出来,写到哪儿算哪儿吧。

嗯,就酱。

0%