理解 “一个 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 差异

  • 在 gevent Worker 中,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 的设计精髓!

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐