当使用Flask进行Web开发,发现接口响应较慢或者超时的时候,我们可以借助性能分析工具line_profiler来查看每行代码的执行时间,从而找到原因。
定义一个装饰器,将line_profiler相关逻辑封装到装饰器中,在需要分析的接口函数加上此装饰器:
from functools import wraps
from flask import Flask, jsonify
from line_profiler import LineProfiler
app = Flask(__name__)
# 打印接口中每行代码执行的时间
def func_line_time(f):
@wraps(f)
def decorator(*args, **kwargs):
profile = LineProfiler()
profile_wrap = profile(f)
result = profile_wrap(*args, **kwargs)
profile.print_stats()
return result
return decorator
@app.route("/")
@func_line_time
def fibonacci():
a = b = 1
for _ in range(2, 100):
a, b = a + b, a
return jsonify(a)
if __name__ == '__main__':
app.run()
启动flask应用,再请求接口,可以看到如下输出:
Timer unit: 1e-09 s
Total time: 0.000516835 s
File: /tmp/pycharm_project_154/app.py
Function: fibonacci at line 22
Line # Hits Time Per Hit % Time Line Contents
==============================================================
22 @app.route("/")
23 @func_line_time
24 def fibonacci():
25 1 1561.0 1561.0 0.3 a = b = 1
26 99 47691.0 481.7 9.2 for _ in range(2, 100):
27 98 97969.0 999.7 19.0 a, b = a + b, a
28 1 369614.0 369614.0 71.5 return jsonify(a)
返回结果中各列的具体含义:
使用kernprof工具,在需要分析的函数前加上装饰器@profile,执行kernprof -l app.py,等接口响应完成后停止flask,kernprof会将分析结果写入app.py.lprof
from flask import Flask, jsonify
from line_profiler.explicit_profiler import profile
app = Flask(__name__)
@app.route("/")
@profile
def fibonacci():
a = b = 1
for _ in range(2, 100):
a, b = a + b, a
return jsonify(a)
if __name__ == '__main__':
app.run()
# 执行
kernprof -l app.py # 执行完后将分析结果写入app.py.lprof
kernprof -l -v app.py # 写入app.py.lprof后打印出来
查看app.py.lprof的内容:
$ python -m line_profiler app.py.lprof
Timer unit: 1e-06 s
Total time: 0.000521002 s
File: app.py
Function: fibonacci at line 7
Line # Hits Time Per Hit % Time Line Contents
==============================================================
7 @app.route("/")
8 @profile
9 def fibonacci():
10 1 1.7 1.7 0.3 a = b = 1
11 99 47.0 0.5 9.0 for _ in range(2, 100):
12 98 78.0 0.8 15.0 a, b = a + b, a
13 1 394.3 394.3 75.7 return jsonify(a)
使用flask应用上下文模拟接口请求过程,无需启动flask应用:
from flask import Flask, jsonify
from line_profiler import LineProfiler
app = Flask(__name__)
@app.route("/")
def fibonacci():
a = b = 1
for _ in range(2, 100):
a, b = a + b, a
return jsonify(a)
if __name__ == '__main__':
with app.app_context():
profile = LineProfiler()
profile.add_function(fibonacci)
profile.enable()
result = fibonacci()
profile.disable()
print(result.get_json())
profile.print_stats()
如果有用到请求上下文中的对象(比如request),可以使用test_client()来模拟:
from flask import Flask, jsonify, request
from line_profiler import LineProfiler
app = Flask(__name__)
@app.route('/')
def fibonacci():
n = int(request.args.get('n'))
a = b = 1
for _ in range(2, n):
a, b = a + b, a
return jsonify(a)
if __name__ == '__main__':
with app.app_context(), app.test_client() as client:
profile = LineProfiler()
profile.add_function(fibonacci)
profile.enable()
result = client.get('/?n=100')
profile.disable()
print(result.get_json())
profile.print_stats()
和前两种比较而言,这种方法不用修改原有代码,无需启动flask应用,也省去了请求接口的步骤,更加方便。