一、先理解核心概念
1.1 什么是 WSGI?
WSGI(Web Server Gateway Interface,Web 服务器网关接口)是 Python Web 应用和 Web 服务器之间的通信标准/协议。
1.2 生活比喻
┌──────────────────────────────────────────────────────────────────┐│ ││ 想象一个餐厅: ││ ││ 顾客(浏览器) ││ ↓ 点菜(HTTP请求) ││ 服务员(Nginx)── 负责接待、传菜 ││ ↓ 把菜单交给厨房 ││ 厨房经理(uWSGI)── 负责调度厨师 ││ ↓ 分配任务 ││ 厨师(Flask/Django)── 负责做菜(处理业务逻辑) ││ ↓ 做好的菜 ││ 厨房经理(uWSGI)── 把菜交给服务员 ││ ↓ ││ 服务员(Nginx)── 把菜端给顾客 ││ ↓ ││ 顾客(浏览器)── 收到菜(HTTP响应) ││ ││ WSGI = 厨房经理和厨师之间的"标准沟通方式" ││ uWSGI = 那个厨房经理(WSGI服务器) ││ │└──────────────────────────────────────────────────────────────────┘
1.3 几个容易混淆的名词
| WSGI | ||
| uWSGI | ||
| uwsgi | ||
| Nginx |
浏览器 ←→ Nginx ←(uwsgi协议)→ uWSGI ←(WSGI标准)→ Flask/Django反向代理 应用服务器 Web框架
1.4 为什么需要 uWSGI?
# Flask 自带的开发服务器(python app.py):# ❌ 单线程,一次只能处理一个请求# ❌ 没有进程管理# ❌ 没有性能优化# ❌ 不能用于生产环境# uWSGI 生产服务器:# ✅ 多进程/多线程,并发处理# ✅ 自动重启崩溃的进程# ✅ 内存管理、请求队列# ✅ 支持负载均衡# ✅ 可以配合 Nginx 使用
二、安装 uWSGI
2.1 系统要求
# uWSGI 需要 C 编译器# Ubuntu/Debiansudo apt updatesudo apt install build-essential python3-dev# CentOS/RHELsudo yum groupinstall "Development Tools"sudo yum install python3-devel# macOSxcode-select --install
2.2 安装 uWSGI
# ============ 方法1:pip 安装(推荐) ============pip install uwsgi# 或者指定 Python 版本pip3 install uwsgi# 在虚拟环境中安装(推荐)python3 -m venv myenvsource myenv/bin/activate # Linux/macOS# myenv\Scripts\activate # Windowspip install uwsgi# ============ 方法2:从源码编译 ============# 下载源码wget https://projects.unbit.it/downloads/uwsgi-latest.tar.gztar xzvf uwsgi-latest.tar.gzcd uwsgi-*# 编译安装python3 uwsgiconfig.py --build defaultsudo cp uwsgi /usr/local/bin/# ============ 验证安装 ============uwsgi --version# 输出:2.0.28(或类似版本号)
2.3 验证安装成功
# 快速测试:启动一个返回 "Hello" 的服务uwsgi --http :9090 --wsgi-file /dev/null --callable nonexistent 2>&1 | head -5# 更好的测试:创建一个测试文件echo 'def application(environ, start_response):start_response("200 OK", )return [b"Hello from uWSGI!"]' > /tmp/test_wsgi.py# 启动uwsgi --http :9090 --wsgi-file /tmp/test_wsgi.py --callable application# 另开终端测试curl http://localhost:9090# 输出:Hello from uWSGI!# 按 Ctrl+C 停止
三、编写最简单的 WSGI 应用
3.1 WSGI 应用的标准格式
WSGI 应用就是一个可调用对象(函数或类),接收两个参数:
def application(environ, start_response):"""参数:environ: 字典,包含请求的所有信息(类似 CGI 环境变量)start_response: 函数,用来设置响应状态码和响应头返回:可迭代对象(通常是列表),包含响应体(bytes)"""# 第一步:设置响应状态码和响应头status = "200 OK"headers =start_response(status, headers)# 第二步:返回响应体(必须是 bytes 的列表)return [b"<h1>Hello, WSGI!</h1>"]
3.2 一个完整的 WSGI 示例
# myapp.py —— 一个完整的 WSGI 应用def application(environ, start_response):"""WSGI 入口函数"""# 从 environ 中获取请求信息method = environ.get("REQUEST_METHOD", "GET")path = environ.get("PATH_INFO", "/")query = environ.get("QUERY_STRING", "")# 根据路径返回不同内容if path == "/":body = """<html><head><title>我的 WSGI 应用</title></head><body><h1>🎉 欢迎来到我的 WSGI 应用!</h1><p>这是一个纯 WSGI 应用,没有使用任何框架。</p><ul><li><a href="/hello">/hello</a> - 打招呼</li><li><a href="/time">/time</a> - 当前时间</li><li><a href="/info">/info</a> - 请求信息</li></ul></body></html>"""elif path == "/hello":# 解析查询参数from urllib.parse import parse_qsparams = parse_qs(query)name = params.get("name", ["世界"])[0]body = f"<h1>你好,{name}!</h1><p><a href='/'>返回首页</a></p>"elif path == "/time":import timenow = time.strftime("%Y-%m-%d %H:%M:%S")body = f"<h1>🕐 当前时间</h1><p>{now}</p><p><a href='/'>返回首页</a></p>"elif path == "/info":# 显示请求信息info_items = [f"<li><b>{key}</b>: {value}</li>"for key, value in sorted(environ.items())if not key.startswith("uwsgi")]body = f"""<html><body><h1>📋 请求信息</h1><ul>{''.join(info_items[:30])}</ul><p><a href='/'>返回首页</a></p></body></html>"""else:start_response("404 Not Found", )return [f"<h1>404 - 页面未找到</h1><p>路径 {path} 不存在</p>".encode("utf-8")]# 设置响应头start_response("200 OK", [("Content-Type", "text/html; charset=utf-8"),("Content-Length", str(len(body.encode("utf-8")))),])# 返回响应体(必须是 bytes 列表)return [body.encode("utf-8")]
3.3 运行这个 WSGI 应用
# 命令行启动(开发测试用)uwsgi --http :8000 --wsgi-file myapp.py --callable application# 参数解释:# --http :8000 → 在 8000 端口启动 HTTP 服务器# --wsgi-file myapp.py → WSGI 应用所在的文件# --callable application → 文件中 WSGI 入口函数的名字# 浏览器访问 http://localhost:8000# 或命令行测试:curl http://localhost:8000curl http://localhost:8000/hello?name=张三curl http://localhost:8000/time
3.4 environ 字典中常用的键
# environ 是一个字典,包含请求的所有信息:{"REQUEST_METHOD": "GET", # 请求方法"PATH_INFO": "/hello", # 路径"QUERY_STRING": "name=张三", # 查询参数"SERVER_NAME": "localhost", # 服务器名"SERVER_PORT": "8000", # 端口"HTTP_HOST": "localhost:8000", # Host 头"HTTP_USER_AGENT": "curl/7.68", # User-Agent"HTTP_ACCEPT": "text/html", # Accept 头"CONTENT_TYPE": "application/json",# 内容类型(POST时)"CONTENT_LENGTH": "42", # 内容长度(POST时)"wsgi.input": <file object>, # 请求体(POST数据)"wsgi.errors": <file object>, # 错误输出流"REMOTE_ADDR": "127.0.0.1", # 客户端IP}
四、uWSGI 配置文件详解
4.1 为什么用配置文件?
命令行参数太多记不住,配置文件可以:
集中管理所有配置 方便版本控制 不同环境用不同配置
4.2 INI 格式配置文件(最常用)
# uwsgi.ini —— 完整配置示例[uwsgi]# ============ 基本设置 ============# 项目目录(所有相对路径基于此)chdir = /home/user/myproject# WSGI 入口文件wsgi-file = myapp.py# WSGI 入口函数名callable = application# ============ 网络设置 ============# 方式1:直接提供 HTTP 服务(开发/测试用)# http = 0.0.0.0:8000# 方式2:通过 uwsgi 协议(配合 Nginx 用,生产推荐)socket = 0.0.0.0:8001# 方式3:通过 Unix Socket(同一台机器,性能最好)# socket = /tmp/myproject.sock# ============ 进程/线程设置 ============# 主进程master = true# 工作进程数(通常 = CPU核心数 × 2 + 1)processes = 4# 每个进程的线程数threads = 2# ============ 稳定性设置 ============# 请求处理完后自动重启进程(防止内存泄漏)max-requests = 5000# 进程空闲超过30秒就重启idle = 30# 进程崩溃后自动重启die-on-term = true# ============ 日志设置 ============# 日志文件路径logto = /var/log/uwsgi/myproject.log# 日志最大大小(超过后轮转)log-maxsize = 10000000# ============ 性能设置 ============# 请求队列大小listen = 128# 缓冲区大小buffer-size = 65535# POST 请求体最大大小(字节)limit-post = 10485760# ============ 其他 ============# 进程文件(记录主进程PID,用于停止/重启)pidfile = /tmp/myproject.pid# 状态文件(可以用 uwsgi --connect-and-read 查看状态)stats = 127.0.0.1:9191# 启用线程(某些框架需要)enable-threads = true# 清空环境(启动时清空继承的环境变量)vacuum = true# 优雅重启的超时时间(秒)harakiri = 60# 请求超时后强制杀死(秒)harakiri-verbose = true
4.3 启动配置文件
# 使用配置文件启动uwsgi --ini uwsgi.ini# 停止(通过 pidfile)uwsgi --stop /tmp/myproject.pid# 重启uwsgi --reload /tmp/myproject.pid# 查看状态uwsgi --connect-and-read 127.0.0.1:9191
4.4 配置参数速查表
chdir | /home/user/project | |
wsgi-file | app.py | |
callable | application | |
module | myapp:application | |
http | :8000 | |
socket | :8001 | |
master | true | |
processes | 4 | |
threads | 2 | |
logto | /var/log/app.log | |
pidfile | /tmp/app.pid | |
max-requests | 5000 | |
harakiri | 60 | |
buffer-size | 65535 | |
vacuum | true | |
die-on-term | true | |
enable-threads | true | |
stats | :9191 | |
chmod-socket | 666 |
五、部署 Flask 应用
5.1 创建 Flask 项目
# 创建项目目录mkdir ~/flask_project && cd ~/flask_project# 创建虚拟环境python3 -m venv venvsource venv/bin/activate# 安装依赖pip install flask uwsgi
5.2 编写 Flask 应用
# app.pyfrom flask import Flask, jsonify, requestimport timeapp = Flask(__name__)@app.route("/")def index():return """<html><head><title>Flask + uWSGI</title></head><body><h1>🚀 Flask 应用已通过 uWSGI 部署!</h1><ul><li><a href="/api/time">/api/time</a> - 当前时间</li><li><a href="/api/info">/api/info</a> - 请求信息</li><li><a href="/api/users">/api/users</a> - 用户列表</li></ul></body></html>"""@app.route("/api/time")def api_time():return jsonify({"timestamp": int(time.time()),"formatted": time.strftime("%Y-%m-%d %H:%M:%S"),"timezone": "Asia/Shanghai"})@app.route("/api/info")def api_info():return jsonify({"method": request.method,"path": request.path,"remote_addr": request.remote_addr,"user_agent": request.user_agent.string,"args": dict(request.args),})@app.route("/api/users")def api_users():users = [{"id": 1, "name": "张三", "age": 25},{"id": 2, "name": "李四", "age": 30},{"id": 3, "name": "王五", "age": 28},]return jsonify({"users": users, "total": len(users)})@app.route("/api/echo", methods=["POST"])def api_echo():data = request.get_json(silent=True) or {}return jsonify({"received": data, "message": "数据已接收"})# ⚠️ 重要:这个 if 块只在直接运行 python app.py 时生效# 通过 uWSGI 启动时不会执行这里if __name__ == "__main__":app.run(debug=True, port=5000)
5.3 创建 WSGI 入口文件
# wsgi.py —— uWSGI 的入口文件from app import app# uWSGI 需要找到一个名为 "application" 的可调用对象# Flask 的 app 对象本身就是 WSGI 可调用对象application = app# 或者更明确地写:# application = app.wsgi_app
📌 为什么要单独创建 wsgi.py? 把入口点和应用逻辑分离,方便部署配置。uWSGI 只需要知道
wsgi.py中的application。
5.4 创建 uWSGI 配置文件
# uwsgi.ini[uwsgi]# 项目目录chdir = /home/user/flask_project# WSGI 入口module = wsgi:application# 等价于:# wsgi-file = wsgi.py# callable = application# 虚拟环境(重要!)virtualenv = /home/user/flask_project/venv# 网络(开发测试用 HTTP,生产用 socket)# 开发:http = 0.0.0.0:8000# 生产(配合Nginx):# socket = 0.0.0.0:8001# chmod-socket = 666# 进程管理master = trueprocesses = 4threads = 2# 稳定性max-requests = 5000harakiri = 60die-on-term = true# 日志logto = /home/user/flask_project/logs/uwsgi.loglog-maxsize = 10000000# PID 文件pidfile = /home/user/flask_project/uwsgi.pid# 其他vacuum = trueenable-threads = truebuffer-size = 65535
5.5 启动和测试
# 创建日志目录mkdir -p logs# 启动uwsgi --ini uwsgi.ini# 测试curl http://localhost:8000/curl http://localhost:8000/api/timecurl http://localhost:8000/api/userscurl -X POST http://localhost:8000/api/echo \-H "Content-Type: application/json" \-d '{"name": "test", "value": 42}'# 停止uwsgi --stop uwsgi.pid# 重启uwsgi --reload uwsgi.pid
5.6 不用配置文件的快速启动
# 一行命令启动(开发测试)cd ~/flask_projectsource venv/bin/activateuwsgi --http :8000 \--wsgi-file wsgi.py \--callable application \--master \--processes 2 \--threads 1# 或者用 module 方式uwsgi --http :8000 --module wsgi:application --master --processes 2
六、部署 Django 应用
6.1 创建 Django 项目
# 创建项目mkdir ~/django_project && cd ~/django_projectpython3 -m venv venvsource venv/bin/activatepip install django uwsgi# 创建 Django 项目django-admin startproject mysite .# 注意末尾的 ".",表示在当前目录创建# 创建一个应用python manage.py startapp blog# 目录结构:# django_project/# ├── venv/# ├── manage.py# ├── mysite/# │ ├── __init__.py# │ ├── settings.py# │ ├── urls.py# │ ├── wsgi.py ← Django 自动生成的 WSGI 入口!# │ └── asgi.py# └── blog/# ├── __init__.py# ├── views.py# ├── models.py# └── ...
6.2 Django 自带的 wsgi.py
# mysite/wsgi.py(Django 自动生成,通常不需要修改)import osfrom django.core.wsgi import get_wsgi_application# 设置 Django 的配置文件路径os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")# 获取 WSGI application 对象application = get_wsgi_application()
6.3 编写一些视图
# blog/views.pyfrom django.http import JsonResponse, HttpResponseimport timedef index(request):return HttpResponse("""<html><body><h1>🎯 Django + uWSGI 部署成功!</h1><ul><li><a href="/api/time/">当前时间</a></li><li><a href="/api/info/">请求信息</a></li></ul></body></html>""")def api_time(request):return JsonResponse({"timestamp": int(time.time()),"formatted": time.strftime("%Y-%m-%d %H:%M:%S"),})def api_info(request):return JsonResponse({"method": request.method,"path": request.path,"remote_addr": request.META.get("REMOTE_ADDR"),"user_agent": request.META.get("HTTP_USER_AGENT"),})
# mysite/urls.pyfrom django.contrib import adminfrom django.urls import pathfrom blog import viewsurlpatterns = [path("admin/", admin.site.urls),path("", views.index),path("api/time/", views.api_time),path("api/info/", views.api_info),]
6.4 修改 settings.py(生产环境)
# mysite/settings.py 中需要关注的配置# 生产环境关闭 DEBUGDEBUG = False# 允许的主机(必须设置!)ALLOWED_HOSTS = ["your-domain.com", "www.your-domain.com", "localhost", "127.0.0.1"]# 静态文件STATIC_URL = "/static/"STATIC_ROOT = "/home/user/django_project/staticfiles/"# 收集静态文件(部署前执行)# python manage.py collectstatic
6.5 创建 uWSGI 配置文件
# uwsgi.ini[uwsgi]# 项目目录chdir = /home/user/django_project# Django 的 WSGI 模块module = mysite.wsgi:application# 虚拟环境virtualenv = /home/user/django_project/venv# 网络# 开发:http = 0.0.0.0:8000# 生产:# socket = 0.0.0.0:8001# chmod-socket = 666# 进程master = trueprocesses = 4threads = 2# 环境变量(确保 Django 能找到配置)env = DJANGO_SETTINGS_MODULE=mysite.settings# 稳定性max-requests = 5000harakiri = 60die-on-term = true# 日志logto = /home/user/django_project/logs/uwsgi.log# PIDpidfile = /home/user/django_project/uwsgi.pid# 其他vacuum = trueenable-threads = truebuffer-size = 65535# 静态文件(让 uWSGI 直接服务静态文件,不经过 Django)static-map = /static=/home/user/django_project/staticfiles
6.6 启动
# 收集静态文件python manage.py collectstatic --noinput# 创建日志目录mkdir -p logs# 启动uwsgi --ini uwsgi.ini# 测试curl http://localhost:8000/curl http://localhost:8000/api/time/
七、配合 Nginx 部署(生产环境)
7.1 架构图
┌─────────────────────────────────────────┐│ 服务器 ││ │浏览器 ──HTTP──→ │ Nginx (:80/:443) ││ ├── 静态文件 → 直接返回 ││ ├── SSL 终止 ││ └── 动态请求 ──uwsgi协议──→ uWSGI ││ (:8001) ││ │ ││ WSGI 标准 ││ │ ││ Flask/Django │└─────────────────────────────────────────┘
7.2 修改 uWSGI 配置(使用 socket)
# uwsgi.ini(生产版本)[uwsgi]chdir = /home/user/flask_projectmodule = wsgi:applicationvirtualenv = /home/user/flask_project/venv# ⚠️ 生产环境用 socket,不用 http!# 因为 Nginx 会处理 HTTP,uWSGI 只需要处理 uwsgi 协议socket = 127.0.0.1:8001# 或者用 Unix Socket(性能更好):# socket = /tmp/myproject.sock# chmod-socket = 664# 进程master = trueprocesses = 4threads = 2# 其他max-requests = 5000harakiri = 60die-on-term = truevacuum = trueenable-threads = truelogto = /home/user/flask_project/logs/uwsgi.logpidfile = /home/user/flask_project/uwsgi.pid
7.3 Nginx 配置
# /etc/nginx/sites-available/myproject# 上游:uWSGI 服务器upstream django {server 127.0.0.1:8001;# 如果用 Unix Socket:# server unix:///tmp/myproject.sock;}server {listen 80;server_name your-domain.com www.your-domain.com;# 字符编码charset utf-8;# 最大上传大小client_max_body_size 75M;# ============ 静态文件 ============location /static/ {alias /home/user/flask_project/static/;expires 30d;add_header Cache-Control "public, immutable";}# ============ 媒体文件 ============location /media/ {alias /home/user/flask_project/media/;}# ============ 动态请求 → uWSGI ============location / {# 将请求转发给 uWSGIuwsgi_pass django;# 包含 uwsgi 参数include /etc/nginx/uwsgi_params;# 超时设置uwsgi_connect_timeout 30;uwsgi_read_timeout 60;uwsgi_send_timeout 60;# 传递真实客户端 IPuwsgi_param X-Real-IP proxy_add_x_forwarded_for;uwsgi_param X-Forwarded-Proto query_string;uwsgi_param REQUEST_METHOD content_type;uwsgi_param CONTENT_LENGTH request_uri;uwsgi_param PATH_INFO document_root;uwsgi_param SERVER_PROTOCOL scheme;uwsgi_param HTTPS remote_addr;uwsgi_param REMOTE_PORT server_port;uwsgi_param SERVER_NAME server_namePROJECT_DIR/venv"echo "🚀 开始部署..."# 1. 进入项目目录cd VENV_DIR/bin/activate# 3. 更新代码git pull origin main# 4. 安装/更新依赖pip install -r requirements.txt# 5. 收集静态文件(如果有)# python manage.py collectstatic --noinput# 6. 创建必要目录mkdir -p logs static# 7. 重启 uWSGIif [ -f uwsgi.pid ]; thenecho " 重启 uWSGI..."uwsgi --reload uwsgi.pidelseecho " 启动 uWSGI..."uwsgi --ini uwsgi.inifi# 8. 重载 Nginxsudo nginx -t && sudo systemctl reload nginxecho "✅ 部署完成!"
11.2 requirements.txt
# requirements.txtflask==3.0.0uwsgi==2.0.28gunicorn==21.2.0 # 备选方案
11.3 项目目录结构
flask_project/├── venv/ # 虚拟环境├── app.py # Flask 应用主文件├── wsgi.py # WSGI 入口├── uwsgi.ini # uWSGI 配置├── requirements.txt # 依赖列表├── deploy.sh # 部署脚本├── logs/ # 日志目录│ └── uwsgi.log├── static/ # 静态文件│ ├── css/│ ├── js/│ └── images/└── templates/ # 模板文件└── index.html
十二、uWSGI vs Gunicorn 对比
# Gunicorn 启动(对比)gunicorn --workers 4 --threads 2 --bind 127.0.0.1:8001 wsgi:application# 对比 uWSGI:# uwsgi --socket 127.0.0.1:8001 --module wsgi:application --master --processes 4 --threads 2
📌 建议:新手/简单项目用 Gunicorn;需要高级功能(缓存、路由、Cron等)用 uWSGI。
十三、速查表
═══════════════════════════════════════════════════════安装═══════════════════════════════════════════════════════sudo apt install build-essential python3-devpip install uwsgiuwsgi --version═══════════════════════════════════════════════════════快速启动═══════════════════════════════════════════════════════# 最简启动uwsgi --http :8000 --wsgi-file app.py --callable application# 带虚拟环境uwsgi --http :8000 --wsgi-file wsgi.py --callable application \--virtualenv /path/to/venv --master --processes 4# 用配置文件uwsgi --ini uwsgi.ini═══════════════════════════════════════════════════════管理命令═══════════════════════════════════════════════════════启动: uwsgi --ini uwsgi.ini停止: uwsgi --stop /path/to/uwsgi.pid重启: uwsgi --reload /path/to/uwsgi.pid状态: uwsgi --connect-and-read 127.0.0.1:9191═══════════════════════════════════════════════════════最小配置模板═══════════════════════════════════════════════════════[uwsgi]chdir = /path/to/projectmodule = wsgi:applicationvirtualenv = /path/to/venvsocket = 127.0.0.1:8001master = trueprocesses = 4threads = 2die-on-term = truevacuum = truelogto = /path/to/logs/uwsgi.logpidfile = /path/to/uwsgi.pid═══════════════════════════════════════════════════════Nginx 关键配置═══════════════════════════════════════════════════════location / {uwsgi_pass 127.0.0.1:8001;include /etc/nginx/uwsgi_params;}location /static/ {alias /path/to/static/;}
夜雨聆风