实测 Flask 的大文件响应:下载 300 MiB 文件时 send_file 内存只多 3 MB、整个读进内存多 303 MB、64 KB 分块生成器多 3 MB 但吞吐更低;send_file 支持 Range 返回 206 与 If-None-Match 返回 304;用 X-Accel-Redirect 让 nginx 发文件,Flask 的 worker 只占 2 ms(代理 send_file 占 170 ms)。
用 ab 压测暴露全局状态问题:读和写之间窗口为 0 时计数一点没丢、窗口 0.0001 秒丢 43%、窗口 0.001 秒丢 79%;遍历 30 万项的全局字典时被并发修改会让 24 个请求 500;共用的 sqlite3 连接这次压测没出错,说明压测过关不等于线程安全。
实测 Flask 开发服务器与生产服务器:启动即打印不能用于生产的警告;轻量接口吞吐差距不大(2224 对 3673 请求/秒)而单线程模式只有 78;64 个各等 0.5 秒的请求在多线程开发服务器 1.6 秒、单线程 33 秒;自动重载与进程模型的差别。
实测用 gunicorn 启动 Flask:app:app 与带参数的工厂写法与写错时的报错、命令行大于 GUNICORN_CMD_ARGS 大于配置文件的优先级、unix socket 监听、用 SIGTTIN 与 SIGTTOU 与 SIGHUP 在运行时调整 worker、--check-config 做部署前检查。
实测 uWSGI 部署 Flask:不写 --callable 时服务起得来但请求 500、线程在这个版本里不加 --enable-threads 也能跑(与文档的说法不同)、默认先加载再 fork 所以 TOKEN 共享而 --lazy-apps 各 worker 不同、--harakiri 2 秒杀掉慢请求并断开连接、--http 直连下 1753 对 gunicorn 3668 请求/秒。
用真实的 nginx 与两个 gunicorn 后端实测反向代理:round-robin 与 weight=3 比 1 的分布、后端停掉时默认重试让 40 个请求全部成功并被动标记不可用、proxy_next_upstream off 时失败 1 个、后端卡住时 GET 超时后被重试而 POST 不被重试(3 个 504)。
实测 ProxyFix 的作用:每个参数各改 WSGI 环境里的哪一项(remote_addr、scheme、host、script_root)、x_for 的数字是从右往左信任几层、x_prefix 让 url_for 与静态文件与重定向带上前缀;用真实的 nginx 把应用挂在 /app/ 子路径时,必须代理传 X-Forwarded-Prefix 且应用的 ProxyFix 信任它。
实测静态文件的生产部署:Flask 的 static 默认 Cache-Control: no-cache(每次 304 确认)、SEND_FILE_MAX_AGE_DEFAULT 设了才有 max-age;nginx 直接发文件 6607 请求/秒对 Flask static 路由 3459;gzip 压到 8.3% 但默认没有 Vary 头;长缓存要靠 URL 带内容哈希失效。
用真实的 nginx 实测媒体文件的生产部署:公开媒体目录由 nginx 直接发并加 nosniff 与 CSP sandbox(应对 SVG 内嵌脚本)、目录不列出;私有文件由 Flask 做权限检查(401、403、404)再用 X-Accel-Redirect 让 nginx 发,internal location 外部直接访问是 404,Range 与中文 Content-Disposition 都正常。
把 Flask 的 Dockerfile 拆成各个步骤逐项验证:真实的 Docker 构建验证 .dockerignore(上下文 52.44MB → 161B、镜像 104.9 MB → 0.026 MB)、层缓存顺序、shell 与 exec 形式 CMD、docker build --check;用本机的 Python 与 Flask 验证 pip install --no-cache-dir、0.0.0.0 与 127.0.0.1 的绑定、PYTHONUNBUFFERED、只读目录下的 PermissionError。没有执行过 docker run。
用真实的 gunicorn 验证健康检查的几个细节:依赖挂掉时 /healthz 仍 200、/readyz 返回 503;sync worker 被慢请求占满时探针等了 2615 ms;依赖卡住时不设超时探针永远等不到结果、设 0.5 秒超时 532 ms 返回 503;自定义 logger_class 让探针不再刷满访问日志。
用真实的 gunicorn 对同一个 3 秒请求发不同信号:SIGTERM 让请求完成(3.01 s)、SIGINT/SIGQUIT 立即打断(500)、SIGKILL master 后孤儿 worker 继续服务;graceful_timeout 小于请求时间时请求被断开且 atexit 不运行;SIGHUP 重载 62 次探测请求失败 0 次。