最近准备学习 Go 的 Web 框架 Gin,然后之前在学习 Python 的框架 FastAPI 的时候经常会听说 FastAPI 的性能可以跟 Go 的框架比一比,因此,为了验证一下这个说法,我把这些框架都拿出来进行了一个简单的压力测试,看看各自性能怎么样。
压测条件
服务器配置
环境 | CPU 核数 | 内存 | 系统 |
---|---|---|---|
服务器 | 2 | 2G | CentOS 7.8 |
本地 | 8 | 16G | macOS 14.1.2 |
启动服务的方式
所有的服务都需要提供相同的接口,实现一个简单的 GET 请求,并且请求可以读取请求参数中的值并返回。
Flask 服务
Flask 的代码 flask-server.py:
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/items/<int:item_id>')
def get_item(item_id):
q = request.args.get('q', '')
return jsonify({
"item_id": item_id,
"q": q
})
if __name__ == '__main__':
app.run(debug=False)
启动方式(需要启动2个进程,充分利用 CPU):
gunicorn flask-server:app -b 0.0.0.0:8012 -w 2
FastAPI 服务
FastAPI 服务代码 fastapi-server.py:
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
启动方式(需要启动2个进程,充分利用 CPU):
gunicorn fastapi-server:app -b 0.0.0.0:8011 -k uvicorn.workers.UvicornWorker -w 2
Tornado 服务
Tornado 服务 tornado-server.py:
import sys
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self, item_id):
q = self.get_argument('q', '')
self.write({
"item_id": item_id,
"q": q
})
def make_app():
return tornado.web.Application([
(r"/items/(\d+)", MainHandler),
])
if __name__ == "__main__":
# python tornado-server.py 8013 2
port, process_num = sys.argv[1:3]
app = make_app()
# 启动多个进程
server = tornado.httpserver.HTTPServer(app)
server.bind(int(port))
server.start(num_processes=int(process_num))
tornado.ioloop.IOLoop.current().start()
启动方式(需要启动2个进程,充分利用 CPU):
python tornado-server.py 8013 2
Sanic 服务
Sanic 服务 sanic-server.py:
import sys
from sanic import Sanic
from sanic.response import json
app = Sanic("server")
@app.route('/items/<item_id:int>')
async def get_item(request, item_id):
q = request.args.get('q', '')
return json({
"item_id": item_id,
"q": q
})
if __name__ == "__main__":
# python sanic-server.py 8014 2
port, process_num = sys.argv[1:3]
app.run(host="0.0.0.0", port=int(port), workers=int(process_num))
启动方式(需要启动2个进程,充分利用 CPU):
python sanic-server.py 8014 2
Gin 服务
Gin 服务代码:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
// 创建一个默认的 Gin 引擎
router := gin.Default()
// 定义路由处理程序
router.GET("/items/:id", func(c *gin.Context) {
// 从路由参数中获取item_id
itemID := c.Param("id")
// 从查询参数中获取q
q := c.Query("q")
// 返回JSON响应
c.JSON(http.StatusOK, gin.H{
"item_id": itemID,
"q": q,
})
})
router.Run(":8022")
}
Gin 服务启动方式:编译成二进制文件直接运行即可。
测试方案
压测工具为 jmeter,压测设置500线程,Ramp-Up设置为0,循环次数设置为永久,每个接口持续压测两分钟,并且记录每个服务的服务器性能状态。
jmeter 聚合报告
Flask 服务器压力
FastAPI 服务器压力
Tornado 服务器压力
Sanic 服务器压力
Gin 服务器压力
这里可以看到,Gin 对于服务器的压力很低,CPU 都还没有利用完,还有很大的利用空间。
结论
- 从这个聚合报告可以看出,flask 的性能是最差的,吞吐量低就算了,异常率也高,而且响应时间也是最大,跟其他几个框架没法比。
- 号称可以跟 go 的框架刚一下的 fastapi 也没有达到预期效果,性能还不如 tornado,而 sanic 倒是真的可以跟 gin 媲美。
- 除了gin 以外,其他框架已经把 CPU 消耗完了,所以数据已经到了瓶颈,而 gin 还有很大的资源空间空闲,这份数据并不是 gin 的真实数据。
- 从报告数据可以看到 sanic 和 gin 的各项数据非常接近,所以在 Python 里面如果真的要追求性能的话,应该选择 sanic 框架。
版权声明:如无特殊说明,文章均为本站原创,转载请注明出处
本文链接:https://tendcode.com/subject/article/Flask-Tornado-FastAPI-Sanic-Gin/
许可协议:署名-非商业性使用 4.0 国际许可协议
Hopetree [博主]
1 楼 - 6月,3 周前
😳我之前一直不太明白为什么我们公司的项目里用Python写的微服务会使用 tornado,通过这次的实验才明白 tornado 是有它的优势的。