知其然,知其所以然
Flask 内部机制 2026-10-02

优雅关闭

用真实的 gunicorn 对同一个 3 秒请求发不同信号:SIGTERM 让请求完成(3.01 s)、SIGINT/SIGQUIT 立即打断(500)、SIGKILL master 后孤儿 worker 继续服务;graceful_timeout 小于请求时间时请求被断开且 atexit 不运行;SIGHUP 重载 62 次探测请求失败 0 次。

Flask gunicorn 优雅关闭 信号 SIGHUP 真实验证 动画演示
Flask 内部机制 2026-10-02

日志配置

app.logger 默认只输出 WARNING 以上,先配根 logger 时 Flask 不再加自己的 handler;gunicorn 下 info 默认丢失,接上 gunicorn.error 的 handler 后格式统一;用 dictConfig 配 JSON 格式并带上请求信息;开发服务器的访问日志来自 werkzeug 的 logger。全部用真实进程运行验证。

Flask 日志 logging dictConfig gunicorn 真实验证 动画演示
Flask 内部机制 2026-10-02

请求 ID 链路追踪

用两个真实的 gunicorn 服务和 nginx 实现请求 ID:入口沿用或生成 ID、写回响应头、通过日志 filter 进每条日志、调用下游时传播;验证忘记传播时链路断掉、不校验客户端 ID 会原样回显 300 字符、后台线程里拿不到 ID、nginx 的 $request_id 与 Flask 日志一致且响应头重复。

Flask 请求ID 链路追踪 日志 nginx 真实验证 动画演示
Flask 内部机制 2026-10-02

错误监控

用真实的 sentry-sdk 2.71.0(自写 Transport 收事件)验证 Flask 错误监控:未处理异常上报、abort(404) 与被 @errorhandler(Exception) 接住的异常不上报、Authorization/Cookie/password 被过滤但 ?token= 与异常消息里的内容不过滤、before_send 与 sample_rate、收集端卡住时请求不变慢但进程退出多等约 2.1 秒。

Flask Sentry 错误监控 sentry-sdk 真实验证 动画演示
Flask 内部机制 2026-10-02

环境变量注入

真实运行验证 Flask 的环境变量注入:from_prefixed_env 把值按 JSON 解析('True' 保留为字符串)、纯数字的 SECRET_KEY 被解析成 int 导致 session 500、后加载的覆盖先加载的、from_envvar 的 RuntimeError 与 silent、.env 只被 flask 命令读取而 gunicorn 不读。

Flask 环境变量 配置 from_prefixed_env python-dotenv 真实验证 动画演示
Flask 内部机制 2026-10-02

多进程日志重复问题

真实运行验证 Flask/gunicorn 的日志重复和丢失:根 logger 与 app logger 都有 handler 输出 2 次、初始化函数重复 addHandler 输出 3 次;gunicorn -w 3 模块顶层日志 3 次、--preload 只有 1 次;开发 reloader 导入 2 次;3 进程写同一个 RotatingFileHandler 9000 行只剩 8215 行,普通追加模式的 FileHandler 完整无缺。

Flask 日志 多进程 gunicorn RotatingFileHandler 真实验证 动画演示
Flask 内部机制 2026-10-02

生产环境 DEBUG=False

验证生产环境 DEBUG=False 的意义:同一个异常在 gunicorn 下返回 265 字节的通用页,在 flask run --debug 下返回 15628 字节、含 Traceback 与源码行的调试页;gunicorn + FLASK_DEBUG=1 没有调试页但 app.debug 已变;DEBUG 开关对渲染耗时影响很小;启动守卫让误开 DEBUG 的服务起不来。

Flask DEBUG 生产环境 gunicorn werkzeug 真实验证 动画演示
Flask 内部机制 2026-10-02

Secret Key 轮换

用两个真实的 gunicorn 实例验证 SECRET_KEY 轮换:直接换 key 会让旧 cookie 全部失效;SECRET_KEY_FALLBACKS 让旧 cookie 仍可读,但只在 session 被重新下发时才换成新 key;一步到位的滚动发布会让新实例签的 cookie 被旧实例拒绝,先加 fallback 再切换的两步走法两个阶段都互相接受;itsdangerous 自签令牌需要自己用密钥列表。

Flask SECRET_KEY 密钥轮换 session itsdangerous 真实验证 动画演示
Flask 内部机制 2026-10-02

数据库连接健康检查

用真实的 PostgreSQL 验证数据库就绪检查:select 1 在数据库停掉时 1 ms 返回 503、恢复后无需重启;就绪检查共用被占满的业务连接池要多等 2497 ms,独立小池只要 10 ms;数据库卡住时不设 connect_timeout/statement_timeout 探针一直等,设了之后分别在约 2 s 与 512 ms 返回 503。

Flask 健康检查 PostgreSQL SQLAlchemy 连接池 真实验证 动画演示
Flask 内部机制 2026-10-02

容器信号处理

用真实进程验证容器里的信号传递问题:SIGTERM 发给 sh 而不是 gunicorn 时(入口脚本没写 exec、sh -c 后面还有别的命令),gunicorn 没有收到信号并成为孤儿;exec gunicorn 与直接运行时 gunicorn 直接收到信号并在 2.56 s 内优雅退出;宽限期比请求短时请求被 SIGKILL 打断。没有在 Docker 容器里验证。

Flask Docker 信号 gunicorn exec 真实验证 动画演示
Flask 内部机制 2026-10-02

Flask 应用启动耗时

真实量 Flask 应用的启动耗时:import flask 106 ms、加一组重的扩展 283 ms;.pyc 缓存让 import 快 4.0 倍;-X importtime 找最贵的包;gunicorn 首个 200 轻应用 108 ms、重 import 266 ms,--preload 没有更快,惰性导入把成本挪给第一次请求;3 秒的导入没有被 --timeout 2 杀掉。

Flask 启动耗时 import gunicorn preload 真实验证 动画演示
Java 内部原理 2026-10-02

Java 类加载:一个 class 文件如何变成可执行的类

用真实的 JDK 21 验证类加载的几个事实:类第一次使用时才加载;加载不等于初始化;缺失的类可能在验证阶段就报 NoClassDefFoundError;HelloWorld 加载 445 个类(其中 441 个来自 CDS 归档);三个内置类加载器的分工。

Java 类加载 JVM CDS 真实验证 动画演示
京ICP备2026067502号-1京公网安备11010502063487号