[Python3高阶编程] - Gunicorn 源码剖析06: 建立整体认知(完整的查看一个 HTTP 请求是如何变成 WSGI 调用的)
步骤输入输出关键函数/对象1TCP 字节流HTTP 请求结构2HTTP 请求WSGIenviron3environ响应体迭代器4响应状态/头/体HTTP 响应字节流write()核心思想:Gunicorn 充当HTTP 协议 ↔ WSGI 接口的“翻译器”。通过这个过程,任何符合 WSGI 标准的 Python Web 应用(Flask、Django、FastAPI 等)都能在 Gunicorn
理解 “一个 HTTP 请求是如何变成 WSGI 调用的”,是掌握 Gunicorn(以及所有 WSGI 服务器)工作原理的关键。
下面以 Gunicorn 的 同步工作进程(SyncWorker) 为例,完整梳理从接收到 HTTP 请求到调用用户 WSGI 应用的全过程。
整体流程概览
HTTP Client
↓ (TCP)
Gunicorn Master (Arbiter) —— 监听 socket(但不处理请求)
↓ fork()
Gunicorn Worker (子进程) —— 继承监听 socket
↓ accept()
Worker 接收新连接 → 读取 HTTP 请求数据
↓ 解析
构建 WSGI environ 字典 + start_response 回调
↓ 调用
user_app(environ, start_response)
↓ 返回
迭代响应体 → 写回 HTTP 响应
↓ close()
关闭连接(或 keep-alive 复用)
分步详解(结合 Gunicorn 源码)
第 0 步:启动App
python gunicorn/app/wsgiapp.py examples.echo:app
# 第一入口: 代码路径: gunicorn/app/wsgiapp.py
# 代码路径: gunicorn/app/wsgiapp.py
def run(prog=None):
"""\
The ``gunicorn`` command line runner for launching Gunicorn with
generic WSGI applications.
"""
from gunicorn.app.wsgiapp import WSGIApplication
WSGIApplication("%(prog)s [OPTIONS] [APP_MODULE]", prog=prog).run()
if __name__ == '__main__':
run()
# 名义入口: gunicorn/app/wsgiapp.py 最终交给了Arbiter().Run处理
# 代码路径: gunicorn/app/wsgiapp.py
class Application(BaseApplication):
def run(self):
# ....
# load cfg
if self.cfg.print_config or self.cfg.check_config:
try:
self.load()
except Exception:
sys.exit(1)
sys.exit(0)
# ...
# 调用父类的 BaseApplication().run
super().run() # 见下面的类
class BaseApplication:
"""
An application interface for configuring and loading
the various necessities for any given web framework.
"""
# ...
def run(self):
try:
Arbiter(self).run() # 最终交给了Arbiter().Run处理
except RuntimeError as e:
print("\nError: %s\n" % e, file=sys.stderr)
sys.stderr.flush()
sys.exit(1)
# 真正的入口: gunicorn/arbiter.py
# 代码文件: gunicorn/arbiter.py
class Arbiter:
# ....
def run(self):
"Main master loop."
# ....
try:
self.manage_workers() # 确保启动指定数量的进程/线程
# Start control socket server after initial workers are spawned
# to avoid fork deadlocks with asyncio
self._start_control_server() # 背景线程接收 commond
while True:
# 处理进程相关的信号
for sig in self.wait_for_signals(timeout=1.0):
signame = self.SIG_NAMES.get(sig)
handler = getattr(self, "handle_%s" % signame, None)
handler()
# ....
# kill 掉空闲的进程或者异常的进程(先 kill, 如果没有killd掉, 下一轮再 kill -9)
self.murder_workers()
self.manage_workers() # 确保稳定提供服务的 进程/线程 数
# 清理和管理由于异常情况(如工作进程被杀、主进程重启等)而导致的“脏”Arbiter 状态。
# 它通常在主循环中被调用,通过检查 Worker 进程的存活状态,清理遗留的信号记录或状态,
# 确保 Arbiter 能正常接收新连接并维持 Worker 的数量,以防止由于状态残留导致系统假死或无法扩容。
self.manage_dirty_arbiter()
except (StopIteration, KeyboardInterrupt):
self.halt()
except HaltServer as inst:
self.halt(reason=inst.reason, exit_status=inst.exit_status)
except SystemExit:
raise
except Exception:
self.log.error("Unhandled exception in main loop", exc_info=True)
self.stop(False)
sys.exit(-1)
第 1 步:Worker 启动并监听 Socket
- Master 进程在启动时创建监听 socket(如
0.0.0.0:8000) - 通过
fork()创建多个 Worker 子进程 (多进程模式,多线程类似) - 每个 Worker 都继承了这个监听 socket
- Worker 启动后进入主循环(
SyncWorker.run())
文件:
gunicorn/workers/sync.py (由worker_class 配置决定, 默认为 sync)其他的 worker_class 都在 “
gunicorn/workers/”下
def run(self):
...
self.socket = self.sockets[0] # 继承自 master 的 socket
while self.alive:
self.accept() # 核心:接受新连接
第 2 步:接受连接并读取 HTTP 请求
def accept(self):
try:
client, addr = self.socket.accept() # 阻塞等待连接
client.setblocking(1)
try:
self.handle(client, addr) # 处理该连接
finally:
client.close()
except ...
accept()返回一个新的 client socket(用于与客户端通信)- 调用
self.handle(client, addr)
第 3 步:解析 HTTP 请求(构建 WSGI environ)
文件:
gunicorn/http/wsgi.py+gunicorn/http/message.py
在 handle() 中:
def handle(self, client, addr):
req = None
try:
# 1. 创建请求解析器
parser = http.RequestParser(self.cfg, client, addr)
# 2. 解析 HTTP 请求行和头部
req = parser.next() # 返回一个 Request 对象
# 3. 构建 WSGI environ 字典
environ = req.to_environ(client) # 👈 关键!
# 4. 定义 start_response 回调
def start_response(status, headers, exc_info=None):
... # 缓存状态和头部,稍后写入 socket
# 5. 调用用户的 WSGI 应用!
resp = self.wsgi(environ, start_response)
# 6. 发送响应
self.write_response(req, resp, ...)
关键:req.to_environ(client) 做了什么?
它根据 HTTP 请求内容,构建符合 PEP 3333 标准的 environ 字典,例如:
environ = {
'REQUEST_METHOD': 'GET',
'PATH_INFO': '/hello',
'QUERY_STRING': 'name=world',
'SERVER_NAME': 'localhost',
'SERVER_PORT': '8000',
'wsgi.version': (1, 0),
'wsgi.url_scheme': 'http',
'wsgi.input': client_file_like_object, # 用于读取 body
'wsgi.errors': sys.stderr,
'wsgi.multithread': False,
'wsgi.multiprocess': True,
'HTTP_HOST': 'localhost:8000',
'HTTP_USER_AGENT': 'curl/7.68.0',
...
}
wsgi.input是一个文件类对象,封装了 client socket,供应用读取 POST body。
第 4 步:调用用户 WSGI 应用
# 用户代码示例
def application(environ, start_response):
status = '200 OK'
headers = [('Content-Type', 'text/plain')]
start_response(status, headers) # 注册响应头
return [b"Hello World"] # 返回响应体(可迭代)
- Gunicorn 将构建好的
environ和start_response传给self.wsgi self.wsgi就是你启动时指定的应用对象(如myapp:app)
第 5 步:收集响应并写回客户端
start_response(status, headers)被调用时,Gunicorn 缓存状态码和响应头- 用户应用返回一个 可迭代对象(如列表、生成器)
- Gunicorn 遍历该对象,将每个 chunk 写入 client socket:
for item in resp:
client.sendall(item)
- 最终发送完整的 HTTP 响应:
HTTP/1.1 200 OK Server: gunicorn/xx.x.x Date: ... Content-Type: text/plain Content-Length: 11 Hello World
补充说明
1. Body 数据如何读取?
- 如果是 POST/PUT 请求,body 数据通过
environ['wsgi.input']提供 - 这是一个
io.BytesIO或自定义的 file-like 对象 - 应用调用
.read()时,底层从 client socket 读取
2. Keep-Alive 支持
- Gunicorn 默认支持 HTTP/1.1 keep-alive
- 在
handle()循环中,同一个连接可处理多个请求(需解析Connection: keep-alive)
3. 异常处理
- 如果用户 app 抛出异常,Gunicorn 捕获并返回
500 Internal Server Error - 错误信息记录到 error log
4. 异步 Worker 差异
- 在
geventWorker 中,accept()和recv()是非阻塞的 - 但 WSGI 调用接口完全一致,这是 WSGI 的优势!
总结:关键转换点
| 步骤 | 输入 | 输出 | 关键函数/对象 |
|---|---|---|---|
| 1 | TCP 字节流 | HTTP 请求结构 | http.RequestParser |
| 2 | HTTP 请求 | WSGI environ |
Request.to_environ() |
| 3 | environ + start_response |
响应体迭代器 | user_app(environ, start_response) |
| 4 | 响应状态/头/体 | HTTP 响应字节流 | write() |
核心思想:Gunicorn 充当 HTTP 协议 ↔ WSGI 接口 的“翻译器”。
通过这个过程,任何符合 WSGI 标准的 Python Web 应用(Flask、Django、FastAPI 等)都能在 Gunicorn 上运行,而无需关心底层网络细节。
这就是 WSGI 的强大之处,也是 Gunicorn 的设计精髓!
更多推荐
所有评论(0)