在 Python3.7 环境下,Uber 开源的 PyFlame 无法使用,而在容器环境中,py-spy 又有诡异的问题。考量各种「采样」工具后,发现只有内置的 cProfile 才是稳定、可靠的。

但是对于复杂的 Flask App,大量使用 cProfile 执行采样非常不方便。本文将给出一个在 Python3.7 的容器环境下经测试可以正常工作的例子。原理是在 app 初始化后,添加 werkzeug ProfilerMiddleware(也许可以翻译成采样中间件)。

我们首先编写一个简单的 Flask App:

"""Profile uwsgi."""

from flask import Flask
from werkzeug.contrib.profiler import ProfilerMiddleware
import json

app = Flask('testapp')


@app.route('/')
def index():
	return json.dumps({'status': 'ok'})


app.config['PROFILE'] = True
app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions = [30], profile_dir='/Users/ringsaturn/Downloads')
app.debug = True
app.run(host='0.0.0.0', port=8080, use_reloader=False)

然后运行这个 App,并访问 localhost:8080

# run server
python3 demo.py

# request
curl "http://localhost:8080"

服务端会对每一个请求,生成一个 prof 文件,并保存到指定的文件夹中。 为了可视化分析文件,需要使用 snakeviz 这个库。命令行中输入:

snakeviz GET.root.000000ms.1566789598.prof

随后会自动在浏览器中打开一个网页,样子如下:

Demo 页面

一些注意事项:

  1. 不同项目,app 的初始化位置不一样,app 很可能在初始化的过程中被封装了一层又一层,务必在最开始的时候做这个操作
  2. ProfilerMiddleware 本质上调用的是 Python 内置的 cProfile 工具,所以需要在和执行采样时的 Python 版本一致的情况下,执行 snakeviz 命令。所以如果在做跨版本之间的比较,会用到虚拟环境或者 pyenv 这样的工具
  3. 保存的目录必须事先存在,否则程序会报错

参考资料: