【接口平台】上报接口处理时间
开发的swapi接口测试平台,有用户反馈有时候页面响应慢,很有可能是自己后台处理的锅,但是并不知道具体在哪些模块出现了问题,故准备接入APM平台,监控接口数据,同时又可以得到并分析用户具体使用的热点功能,方便后续的规划和优化
因为APM暂时未有支持python后台的上报框架,需要直接对接原始的上报接口
如何做到将所有的接口数据(包括响应时间、状态码、请求的数据等信息)均上报apm
开始想尝试使用装饰器+异步处理实现,还有网友的跳坑经历
def cost_count(func):
@wraps(func)
def wraper(*args, **kw):
a = time.time()
func(*args, **kw)
# return func(*args, **kw)
print(time.time()-a)
return wraper
@app.route("/view/<slug>", methods=['GET'])
@cost_count
def page_view(slug):
return render_template("view.html",entry=entry)
这样即使填了坑,也需要在每个路由加上装饰器,麻烦且低效
flask好歹也是一个相对强大的框架,是否有什么方式可以支持呢?
发现或许before_request 和 after_request可以解决问题
@app.before_request
def before_request(request):
pass
@app.after_request
def after_request(response):
pass
在before_request的时候记录开始请求的时间,after_request进行异步上报,既可以完美实现,又不影响性能
flask又提供了异步处理的方法
from flask import Flask
import time
from concurrent.futures import ThreadPoolExecutor
executor = ThreadPoolExecutor(1)
app = Flask(__name__)
@app.route('/synchronize')
def update_redis():
executor.submit(do_update)
return 'ok'
def do_update():
time.sleep(10)
print('start update')
if __name__ == '__main__':
app.run()
但是有个问题,在before_request中需要传递接口开始处理的时间戳,但是怎么将值传给after_request呢?
开始想着赋值给一个request.headers,然后直接异常抛出
发现在和前端定义接口时,header中必须有时间的变量,使用的时候才发现,当时定义的时间是秒级别的,但是现在需要至少毫秒级别的时间或时间戳,故此方案行不通
后面发现其实可以赋值给app = Flask(name,) 的全局变量,然后在处理该请求的线程中传递变量即可
@app.before_request
def before_request():
# 将当前时间戳赋值给app全局变量
app.g = int(round(time.time() * 1000))
@app.after_request
def after_request(response):
# 异步上报apm数据
executor.submit(ApmCollect().collectReq,(request.method,request.path,response.status_code,app.g))
# 返回请求响应
return response
在本地windows环境中调试ok后部署到正式环境,发现没有上报数据,尴尬
打日志查看原因,发现 ApmCollect().collectReq 方法中的request请求并未执行,且未有任何的异常报错
requests.post(APM_COLLECT_URL, headers=headers, json=body, verify=False, timeout=10)
执行其他语句均没有问题,执行任意的requests.post请求均未有任何响应
将requests.post放在before_request和after_request中执行,均可正常执行,看来问题出在executor.submit异步执行上
在网上在原因的时候发现,有网友得到这样的结论
flask自带的开发服务器默认是不开启多线程的,浏览器A请求时已经占用了唯一的“线程”,
而在这个请求中又通过requests.post发起了一个请求B,但此时已经没有可用的“线程”了,
于是出现了B在等待A释放“线程”,A在等待B返回,于是一直卡死在那
不过在文章后面又推翻了自己的结论,尴尬
是否是因为 executor.submit 的调用时候出现了问题,换个调用方式
# 异步上报apm数据
executor.submit(ApmCollect().collectReq(request.method,request.path,response.status_code,app.g))
问题竟然解决了