【接口平台】上报接口处理时间

开发的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))

问题竟然解决了

posted @ 2019-10-08 22:15  guanqinghua  阅读(30)  评论(0编辑  收藏  举报