响应变化,是敏捷的美德。
团队一直采用python-flask框架做web开发,最近业务扩大规模,需要能够承载峰值为10000的qps,团队准备用GO语言写API接口,在此之前,我使用python3.5的新语法async/await改写了API,对协程做了初步的探索。
代码都比较简单,我直接放在下面,让大家对新老API有个直观的了解。
FLASK框架的API(以下称老接口):
from flask import Flask, request, jsonify
from models import get_question_analysis
app = Flask(__name__)
@app.route('/', methods=['GET'])
def index():
return 'hello world'
@app.route('/api', methods=['POST'])
def api():
data = request.get_json()
question = data['question']
question_analysis = get_question_analysis(question)
return jsonify(question_analysis)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=False)
使用ASYNC语法,aiohttp框架下的API(以下称新接口):
from aiohttp import web
from models import get_question_analysis
async def index(request):
return web.Response(text='Hello World')
async def api(request):
data = await request.json()
question = data['question']
question_analysis = await get_question_analysis(question)
return web.json_response(question_analysis)
app = web.Application()
app.add_routes([web.get('/', index),
web.post('/api', api)])
if __name__ == '__main__':
web.run_app(app, host='0.0.0.0', port=5000)
针对两个接口,我用了两种方法做测试,过程如下:
方法一:用本机(macOS)ab(apache benchmark)直接做性能测试
测试方法:在命令行输入ab -c [concurrency] -n [total requests] T 'application/json' —p [filename] http://localhost:5000/api
通过更改"每秒并发数"和"服务进程数",得知:
- 12个进程,500并发,老接口的理论qps值为550,而新接口的理论qps值为4023,提高了7倍
-
1个进程,100并发,老接口的理论qps值为88,而新接口的理论值为522,提高了6倍
优点:
- ab很简单,macOS与linux命令行原生自带,直接可用,数据清晰
缺点:
- 本机测试无法准确给出单个request的响应时间,实际业务中timeout的情况无法准确分析;
- qps值为理论值,实际业务中程序调度,机器负荷的花费无法考虑
方法二:用grafana.***.net做压力测试
测试方法:(蔽司的一个压测工具)模拟线上请求对API打压
数据分析:在打压API时,会返回每次请求的响应时间csv
为了满足业务50%请求的平均响应时间都在10ms以内的要求,我分别对新老接口(12个进程/台)进行了压力测试:
-
新接口
- 老接口({qps: 400, avg: 25}, {qps: 300, avg: 14} {qps: 200, avg: 13})
可以看到,在3500的并发下,新接口都能保持良好的性能,50%的请求都能在7ms内完成,并且响应时间的均值为8ms;另一方面,老接口的性能就难以保证,在400并发下,平均响应时长高达27ms。
到现在,我们应该知道python async的性能是非常优秀的,在我看来,接口的性能仍然有提升的空间,希望以后能够有更多的时间来探索、测试和分享。