目录

flask入门五WSGI及其Python实现

flask入门(五)WSGI及其Python实现

WSGI(Web Server Gateway Interface)

WSGI:Python Web 应用(Flask、Django、FastAPI 等)与 Web 服务器(Gunicorn、uWSGI、mod_wsgi 等)之间的标准协议

https://i-blog.csdnimg.cn/direct/e228e99a23b84e72a8c69aab9162885b.png#pic_center

为什么需要 WSGI?

  • Nginx / Apache → 负责接收浏览器的 HTTP 请求
  • Flask / Django → 负责写业务逻辑,返回 HTML/JSON

❌ 问题:Nginx 并不能直接“调用” Python 代码,它只会转发 HTTP 请求
✅ 解决:定义一个“桥梁”—— WSGI 协议

浏览器 ← \leftarrow ←HTT → \to →Nginx/Apache ← \leftarrow ←WSGI → \to →Gunicorn/uWSGI ← \leftarrow ←Python → \to →Flask/Django

WSGI应用本质上是一个Python可调用对象(通常是函数或类的实例),它必须接受两个参数

  • environ:一个包含请求数据的Python字典
  • start_response:一个用于发送HTTP状态和头信息的回调函数

这个接口设计得非常简单,只要求Web开发者实现一个函数,就可以响应HTTP请求
WSGI是一个低级接口,许多Python开发者在使用Django、Flask等流行框架时,实际上是在WSGI之上构建应用
最基础的WSGI应用实现

def application(environ, start_response):
    """一个简单的WSGI应用"""
    # 设置HTTP响应状态和头信息
    status = '200 OK'
    headers = [('Content-Type', 'text/plain; charset=utf-8')]
    
    # 调用start_response发送响应头
    start_response(status, headers)
    
    # 获取请求路径
    path = environ.get('PATH_INFO', '').lstrip('/')
    
    # 根据路径返回不同内容
    if path == '':
        return [b'Welcome to WSGI application!']
    elif path == 'hello':
        return [b'Hello, WSGI World!']
    else:
        start_response('404 Not Found', [('Content-Type', 'text/plain')])
        return [b'404 Not Found']

# 运行WSGI服务器
if __name__ == '__main__':
    from wsgiref.simple_server import make_server
    httpd = make_server('', 8000, application)
    print("Serving on port 8000...")
    httpd.serve_forever()
  • WSGI应用函数:application函数是核心,它接收environstart_response两个必需参数
  • environ字典:包含所有请求信息,如HTTP方法、路径、头信息等。可以通过environ.get('PATH_INFO')获取请求路径
  • start_response函数:用于设置HTTP响应状态和头信息。它需要两个参数:状态字符串(如'200 OK')和头信息列表(如[('Content-Type', 'text/plain')]
  • 响应体:必须是一个字节字符串的可迭代对象(通常是列表),如[b'Hello, WSGI!']
  • WSGI服务器:使用Python标准库中的wsgiref模块创建一个简单的WSGI服务器来运行我们的应用

实际工作流程

  1. 当用户访问服务器时,WSGI服务器接收HTTP请求
  2. 服务器将请求信息转换为environ字典
  3. 服务器调用WSGI应用函数,传入environstart_response
  4. WSGI应用处理请求,调用start_response设置响应头
  5. WSGI应用返回响应体(字节字符串列表)
  6. 服务器将响应发送回客户端

基于WSGI实现的Flask框架

前置知识

Flask框架本身就是构建在WSGI规范之上的,每个Flask应用实例本质上就是一个符合WSGI规范的可调用对象
在Flask内部,Flask类实现了WSGI接口

  • Flask应用实例是一个可调用对象(实现了__call__方法)
  • 当WSGI服务器调用该实例时,会传入environstart_response参数 → \to →app(environ, start_response)可以被服务器调用
  • Flask内部处理请求,生成响应,并返回符合WSGI规范的响应体

基本Flask应用(标准用法):

from flask import Flask, request

app = Flask(__name__)

@app.route('/')
def home():
    return 'Hello from Flask!'

@app.route('/user/<username>')
def show_user_profile(username):
    return f'User: {username}'

@app.route('/query')
def query_example():
    name = request.args.get('name', 'Guest')
    return f'Hello, {name}!'

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=5000)
  • 用 Flask 内置服务器(调试)
python app.py

Flask 内部还是调用 WSGI 接口,只是用内置开发服务器封装了一层
但在生产环境下如果用 nohup python app.py&之类运行,Ctrl+C后台挂起/终端断开/内存不足时进程会挂掉。

  • 方式 B:用原生 WSGI 服务器启动
# wsgi_server.py
from wsgiref.simple_server import make_server
from app import app   # app就是Flask实例,也是WSGI应用

httpd = make_server('', 5000, app)   # 这里app直接当WSGI应用传入
print("Serving Flask on port 5000...")
httpd.serve_forever()
  • 方式 C:用 Gunicorn 启动
gunicorn -w 4 -b 0.0.0.0:5000 app:app

生产环境中的WSGI

在实际生产环境中,我们通常不会使用wsgiref这样的简单服务器,而是使用更强大的WSGI服务器如GunicornuWSGI,它们能处理更多并发请求并提供更好的性能

# 使用Gunicorn运行Flask应用
gunicorn -w 4 -b 0.0.0.0:5000 app:app
  • gunicorn:启动 Gunicorn 服务器的命令
  • -w 4:表示 worker 数量,每个 worker 是一个独立的 Python 进程(不是线程),可以并发处理多个请求,多 worker 能更好利用多核 CPU
  • -b 0.0.0.0:5000:表示绑定地址和端口 → \to →服务地址是 http://服务器IP:5000/
  • app:app:模块名:应用对象
    • 第一个 app → Python 文件名(不带 .py),即 app.py
    • 第二个 app → 文件里的 Flask 应用对象

参考文献