最直接方式是在process_request和process_response中用time.perf_counter()打时间戳并计算差值,需覆盖异常路径,避免i/o,推荐接入prometheus记录耗时分布。

用 Django Middleware 记录每个请求的耗时
最直接的方式是在请求进入和离开时打时间戳,中间差值就是接口响应耗时。Django 的中间件天然适合这个场景,且不侵入业务逻辑。
关键点在于:必须在 process_request 和 process_response 中分别记录时间,不能只依赖 process_view(它不覆盖 404、静态文件等路径),也不能用装饰器——漏掉 admin、APIView 子类、DRF 路由等常见入口。
- 推荐在
settings.py的MIDDLEWARE列表最顶部插入自定义中间件,确保覆盖所有请求 - 用
time.perf_counter()而非time.time(),避免系统时钟回拨导致负耗时 - 注意异常路径:
process_exception里也要记录耗时,否则 500 错误的请求会丢失指标 - 不要在中间件里做 I/O 操作(如写文件、发 HTTP 请求),否则会拖慢所有请求;只存内存或发到日志/监控系统
把耗时数据送到 Prometheus 或 ELK
单纯打印到 console 或 log 文件没意义,生产环境需要聚合、报警和趋势分析。Prometheus + Grafana 是目前最轻量可靠的组合,ELK(Elasticsearch + Logstash + Kibana)适合已有日志体系的团队。
如果选 Prometheus,核心是暴露一个 /metrics 端点,并用 Summary 类型指标记录耗时分布(比 Gauge 更合适,能自动计算 p95/p99):
SkillSub Pro - Python 题解与代码注释双功能技能功能概述SkillSub Pro - Python 题解与代码注释双功能技能是一项面向实际任务的技能,主要用于SkillSub Pro 是一个 Python 题解生成与代码注释的 双功能合体技能 ,专为学生、算法学习者和开发者设计;✅ 一个技能,两种用途 :;核心要点📝 题解模式 :输入题目/题号,自动生成完整 Python 题解(含详细注释、解题思路、复杂度分析);💬 注释模式 :输入 Python 代码,自动添加详细中。它将相关步骤、
from prometheus_client import Summary
<p>REQUEST_TIME = Summary('django_http_request_duration_seconds', 'Time spent processing request', ['method', 'endpoint'])</p><p>def my_middleware(get_response):
def middleware(request):
start_time = time.perf_counter()
response = get_response(request)
duration = time.perf_counter() - start_time
REQUEST_TIME.labels(
method=request.method,
endpoint=request.resolver_match.view_name or 'unknown'
).observe(duration)
return response
return middleware
</p>
-
view_name可能为None(如 404、静态文件),务必 fallback - 避免在
observe()前做复杂字符串拼接,影响性能 - Prometheus 默认每 15 秒 scrape 一次,所以
/metrics响应要快(
警惕 DRF 和异步视图的耗时偏差
Django 4.1+ 支持异步视图,DRF 的 APIView 默认仍是同步执行但内部可能触发异步 IO(如数据库查询)。这时用同步中间件测出的“耗时”其实是整个请求生命周期,但真正瓶颈可能在 DB 或缓存层,而非 Python 执行本身。
- 对 async view,中间件也得是 async 的:
async def __call__,否则会阻塞 event loop - DRF 的
APIView.dispatch会额外包装一层,resolver_match.view_name可能返回'api_view'而非真实路由名,建议改用request.path或提取 URL pattern - 若用了
django-redis或psycopg2-binary,记得确认是否启用连接池;否则耗时波动大,不是代码问题而是连接开销
别忽略静态文件和健康检查接口的干扰
生产环境常把 /static/、/healthz、/favicon.ico 这类请求也计入监控,结果拉低平均耗时、掩盖真实业务接口问题。
- 在中间件里加白名单或黑名单判断:
if request.path.startswith('/static/') or request.path in ['/healthz', '/readyz']:直接跳过记录 - 注意 Django 的
STATIC_URL可能不是/static/,应读取配置而非硬编码 - 某些前端构建产物会高频请求
/favicon.ico,这类 404 请求虽快,但数量大时会刷高 QPS,建议 Nginx 层直接返回空响应,不进 Django
真正难的是区分“慢是代码写的差”,还是“慢是数据库锁了”,或是“慢是 CDN 回源超时”。监控只是第一步,后续得配合 DB 慢查询日志、APM 工具(如 Sentry Performance)才能定位根因。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










