当前位置:首页 > 文章列表 > 文章 > python教程 > Python Flask 实战:别把请求上下文当全局变量用

Python Flask 实战:别把请求上下文当全局变量用

来源:Python 博主原创 2026-06-04 14:17:24 0浏览 收藏

Flask 项目小的时候,很多写法看起来都没问题:模块顶层连数据库、到处读环境变量、后台线程里顺手用 request,视图函数里临时拼配置。等服务上了多进程、多线程和灰度发布,问题就开始冒出来:连接泄漏、配置不一致、请求对象跨线程失效、偶发的“working outside of request context”。

这篇文章不写 Flask 入门,而是按生产排障视角讲清楚:Flask 的应用上下文和请求上下文到底该怎么用,为什么 g 适合请求内资源缓存,为什么资源释放要交给 teardown,配置为什么要集中到 app factory,而不是散落在代码各处。

Python Flask 生产上下文治理思维导图
治理思路:用 app factory 收口配置,用 request/app context 管边界,用 g 和 teardown 管资源生命周期。

业务场景:订单接口偶发连接耗尽

假设一个 Flask 订单服务最开始只有几个接口,开发为了省事,把数据库连接放在模块全局变量里。测试环境一切正常,上线后跑在多 worker 下,连接数开始莫名上涨。

# app.py
from flask import Flask, jsonify

app = Flask(__name__)
db = connect_db(app.config["DATABASE_URL"])

@app.get("/orders")
def orders():
    rows = db.query("select * from orders limit 20")
    return jsonify([dict(row) for row in rows])

这个写法最大的问题是生命周期不清楚:连接什么时候创建、被哪个进程持有、失败后怎么重建、请求结束后是否释放,都没有统一答案。Flask 给我们的上下文机制,就是用来把这些边界管清楚的。

先分清两层上下文

Flask 里常用的 current_appgrequestsession 都不是普通全局变量,它们是上下文局部对象。请求进来时,请求上下文和应用上下文被推入;请求结束后,再按顺序清理。

Python Flask 请求上下文生命周期流程图
生命周期:请求内可以使用 request、session、g;请求结束后 teardown 负责清理资源。

理解这一点后,就不会把 request 传给后台线程,也不会把 g 当跨请求缓存。它们的价值是“请求内稳定可用,请求后自动释放”,不是让你绕开参数传递。

用 app factory 收口配置

生产项目里,我更倾向用 create_app 创建应用,并在里面集中加载配置。默认配置、实例目录配置、环境变量、命令行覆盖,各有优先级,但不要在业务代码里到处读取。

# app_factory.py
from flask import Flask

def create_app(config_object: str | None = None) -> Flask:
    app = Flask(__name__, instance_relative_config=True)
    app.config.from_mapping(
        DATABASE_URL="sqlite:///local.db",
        REQUEST_TIMEOUT=2.0,
    )
    app.config.from_pyfile("config.py", silent=True)
    if config_object:
        app.config.from_object(config_object)
    register_routes(app)
    register_teardown(app)
    return app

这样做的好处是测试、开发、生产都能明确知道配置从哪里来。更重要的是,业务模块不再偷偷读取环境变量,排查“为什么这个 worker 配置不一样”时少很多噪音。

用 g 做请求内资源缓存

数据库连接、轻量客户端、一次请求内需要复用的对象,可以放到 g。它不是全局缓存,而是当前上下文中的临时存储。

from flask import current_app, g

def get_db():
    if "db" not in g:
        g.db = connect_db(current_app.config["DATABASE_URL"])
    return g.db

@app.get("/orders")
def orders():
    rows = get_db().query("select * from orders limit 20")
    return jsonify([dict(row) for row in rows])

这个模式很适合“请求内复用,请求后释放”。同一次请求多次调用 get_db() 不会重复建连接;下一个请求会拿到新的上下文,不会共享上一次请求的临时状态。

Python Flask 上下文与配置风险写法对照
代码审查重点:全局连接、跨线程传 request、配置散落,都是 Flask 生产故障的常见入口。

资源释放交给 teardown

只创建不关闭,是 Flask 小项目变成生产服务后最常见的坑。正确做法是注册 teardown 回调,让请求结束或应用上下文弹出时统一释放资源。

def close_db(error: BaseException | None = None) -> None:
    db = g.pop("db", None)
    if db is not None:
        db.close()

def register_teardown(app: Flask) -> None:
    app.teardown_appcontext(close_db)

注意 teardown 回调不应该假设前面的钩子一定执行成功。线上请求可能在路由前、视图中、响应构造时任意阶段失败,清理逻辑要写得幂等,拿不到资源就直接返回。

不要把 request 传给后台线程

有些接口响应后要发通知,于是有人把 requestcurrent_app 直接塞给线程。这个写法很危险,因为请求上下文结束后,对象就不再处于可用边界内。

# 风险写法:后台线程使用请求上下文对象
threading.Thread(target=send_notice, args=(request,)).start()

更稳的做法是只提取必要的普通数据,比如用户 ID、请求 ID、业务参数,再交给队列或后台任务。关键任务还要有重试、幂等和告警,不要靠一个临时线程赌运气。

payload = {
    "user_id": g.user_id,
    "request_id": request.headers.get("X-Request-ID"),
}
enqueue_notice(payload)

诊断步骤:从上下文错误和连接数开始查

Flask 服务出现偶发上下文错误或连接数持续上涨,我通常按这个顺序查:

  • 搜索模块级连接、模块级客户端、模块导入时读取配置的代码。
  • 检查后台线程、定时任务、回调里是否直接使用 requestgcurrent_app
  • 检查数据库连接是否通过 g 获取,并在 teardown 中关闭。
  • 检查 teardown 是否幂等,异常请求是否也能释放资源。
  • 用压测观察连接数、P99、错误率、请求 ID 日志是否连续。

上线检查清单

  • 应用统一由 create_app 创建,配置加载路径清晰可追踪。
  • 业务代码不在模块导入阶段连接数据库或读取动态配置。
  • 请求内资源通过 g 缓存,避免重复创建。
  • 资源释放统一注册到 teardown_appcontext,并保证幂等。
  • 后台任务只接收普通数据,不接收 requestg 等上下文对象。
  • 测试覆盖 test_client、配置覆盖、异常请求下资源释放。
  • 线上日志包含 request id,指标覆盖连接数、P99、异常类型和 teardown 错误。

总结

Flask 的轻量不是随便写的理由。它把很多边界交给工程师自己决定:应用怎么创建,配置从哪里来,请求内资源怎么复用,请求结束怎么清理,后台任务怎么脱离上下文。

我的经验是,Flask 项目越小,越要早点把这些规矩立住。app factory、g、teardown、集中配置并不复杂,但它们能让一个 Python 小服务在多进程、多线程和灰度发布里少很多隐性故障。

版本声明
本文转载于:Python 博主原创 如有侵犯,请联系study_golang@163.com删除
MySQL 8.4 复制延迟排障:别只盯 Seconds_Behind_SourceMySQL 8.4 复制延迟排障:别只盯 Seconds_Behind_Source
上一篇
MySQL 8.4 复制延迟排障:别只盯 Seconds_Behind_Source
Arthas 排查 CPU 飙高:thread、trace、watch 连起来定位慢方法
下一篇
Arthas 排查 CPU 飙高:thread、trace、watch 连起来定位慢方法
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • ChatExcel酷表:告别Excel难题,北大团队AI助手助您轻松处理数据
    ChatExcel酷表
    ChatExcel酷表是由北京大学团队打造的Excel聊天机器人,用自然语言操控表格,简化数据处理,告别繁琐操作,提升工作效率!适用于学生、上班族及政府人员。
    5963次使用
  • Any绘本:开源免费AI绘本创作工具深度解析
    Any绘本
    探索Any绘本(anypicturebook.com/zh),一款开源免费的AI绘本创作工具,基于Google Gemini与Flux AI模型,让您轻松创作个性化绘本。适用于家庭、教育、创作等多种场景,零门槛,高自由度,技术透明,本地可控。
    6383次使用
  • 可赞AI:AI驱动办公可视化智能工具,一键高效生成文档图表脑图
    可赞AI
    可赞AI,AI驱动的办公可视化智能工具,助您轻松实现文本与可视化元素高效转化。无论是智能文档生成、多格式文本解析,还是一键生成专业图表、脑图、知识卡片,可赞AI都能让信息处理更清晰高效。覆盖数据汇报、会议纪要、内容营销等全场景,大幅提升办公效率,降低专业门槛,是您提升工作效率的得力助手。
    6193次使用
  • 星月写作:AI网文创作神器,助力爆款小说速成
    星月写作
    星月写作是国内首款聚焦中文网络小说创作的AI辅助工具,解决网文作者从构思到变现的全流程痛点。AI扫榜、专属模板、全链路适配,助力新人快速上手,资深作者效率倍增。
    8167次使用
  • MagicLight.ai:叙事驱动AI动画视频创作平台 | 高效生成专业级故事动画
    MagicLight
    MagicLight.ai是全球首款叙事驱动型AI动画视频创作平台,专注于解决从故事想法到完整动画的全流程痛点。它通过自研AI模型,保障角色、风格、场景高度一致性,让零动画经验者也能高效产出专业级叙事内容。广泛适用于独立创作者、动画工作室、教育机构及企业营销,助您轻松实现创意落地与商业化。
    6773次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码