Adi

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

本节说明

本节的标题是——协议,有两层意思,一个是用户请求的协议 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 默认实现了 synceventletgeventtornadogthread 这几种类型 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%