gin sanic flask 性能对比

603 阅读2分钟

最近想要看下go与python的性能到底有多少差异,不比不知道,差距还是蛮大的,为了方便实验,两个后台服务都访问相同的redis服务器, 简单的使用get 命令,获取一个值, 再通过json 格式返回

go 代码 使用gin 框架

package main

import (
	"context"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis/v8"
)

var (
	Redis *redis.Client
)

func InitRedis() *redis.Client {
	rdb := redis.NewClient(&redis.Options{
		Addr:     "127.0.0.1:6379",
		Password: "xxxx", // no password set
		DB:       10,                 // use default DB
		PoolSize: 10,
	})
	result := rdb.Ping(context.Background())
	fmt.Println("redis ping:", result.Val())
	if result.Val() != "PONG" {
		// 连接有问题
		return nil
	}
	return rdb
}

func indexHandler(c *gin.Context) {
	name, err := Redis.Get(c, "name").Result()
	if err != nil {
		c.JSON(500, gin.H{
			"msg": err.Error(),
		})
	} else {
		c.JSON(200, gin.H{
			"name": name,
		})
	}

}

func main() {
	e := gin.New()
	e.Use(gin.Recovery())

	Redis = InitRedis()
	e.GET("/", indexHandler)
	e.Run(":8080")
}

python 代码 使用sanic 框架

# -*- coding: utf-8 -*-
from sanic import Sanic, Request
from sanic import response
from sanic_ext import validate
from pydantic import BaseModel
import aioredis

app = Sanic("test")


async def beforestart(app: Sanic):
    address = f'redis://:xxxx@127.0.0.1:6379/10'
    pool = aioredis.ConnectionPool.from_url(address)
    redis = aioredis.Redis(connection_pool=pool, encoding='utf-8')
    app.ctx.redis = redis

@app.get("/")
async def test(r: Request):
    try:
        name = await r.app.ctx.redis.get("name")
        return response.json({"name": name.decode()})
    except:
        return response.json({"name": "err"}, status=500)

app.register_listener(beforestart, "before_server_start")

if __name__ == '__main__':
    app.run(host="0.0.0.0", port=8090, access_log=False, auto_reload=True)

压测命令为 ./wrk -t8 -c100 -d10s --latency "http://127.0.0.1:8080/"

gin 的压测结果

  49701 requests in 10.01s, 6.49MB read
Requests/sec:   4967.20
Transfer/sec:    664.56KB

python 的压测结果

  27415 requests in 10.01s, 2.85MB read
Requests/sec:   2737.51
Transfer/sec:    291.40KB

gin 是 sanic 的 1.7倍, 差距还是很大的 我尝试在sanic 中使用多workers来启动,但是压测结果和使用单workers 差不多。

再看看老牌的flask 性能如何?

from flask import Flask
from flask_redis import FlaskRedis

import logging
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)

app = Flask(__name__)
app.config['REDIS_URL'] = 'redis://:xxxx@127.0.0.1:6379/10'
redis_client = FlaskRedis(app)

@app.route('/')
def index():
    name = redis_client.get('name')
    return {"name": name.decode()}

if __name__ == '__main__':
    app.run(debug=False)

压测结果为

3685 requests in 10.01s, 647.75KB read
Requests/sec:    368.14
Transfer/sec:     64.71KB

好低呀

我们可以使用多线程或者多进程来提高flask 的并发能力

# 1.threaded : 多线程支持,默认为False,即不开启多线程;
app.run(threaded=True)
# 2.processes:进程数量,默认为1.
app.run(processes=True)
ps:多进程或多线程只能选择一个,不能同时开启

多线程的结果,比单线程稍好一些

  4939 requests in 10.01s, 868.18KB read
Requests/sec:    493.39
Transfer/sec:     86.73KB

多进程的结果

  3655 requests in 10.01s, 642.48KB read
Requests/sec:    365.22
Transfer/sec:     64.20KB

和单线程结果差不多

使用gevent做协程,并且使用多进程

from flask import Flask
from flask_redis import FlaskRedis
from gevent import monkey
from gevent.pywsgi import WSGIServer
monkey.patch_all()
from multiprocessing import cpu_count, Process
import logging
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)

app = Flask(__name__)
app.config['REDIS_URL'] = 'redis://:xxxx@127.0.0.1:6379/10'
redis_client = FlaskRedis(app)

@app.route('/')
def index():
    name = redis_client.get('name')
    return {"name": name.decode()}

if __name__ == '__main__':
    #app.run(debug=False, host="0.0.0.0", processes=True)
    mulserver = WSGIServer(('0.0.0.0', 5000), app)
    mulserver.start()
    def server_forever():
        mulserver.start_accepting()
        mulserver._stop_event.wait()

    for i in range(cpu_count()):
        p = Process(target=server_forever)
        p.start()

此时结果是flask 中最好的

8650 requests in 10.01s, 1.02MB read
Requests/sec:    864.09
Transfer/sec:    104.33KB

做张表来对比一下

框架说明Requests/secTransfer/sec
flask单线程36864kb
flask多线程49386kb
flaskgevent 协程864104kb
sanic单workers与多workers 数据差不多2737291kb
gin4967664kb

总结

  1. 单纯的以qps为指标来看,go 对于 python 来说还是处于碾压状态
  2. flask 结合 gevent 性能上有很大的提升
  3. 即使flask使用了gevent,但是与sanic和gin 还是有很大的差距