gin 、sanic、flask 读取 redis 并发对比

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

go 代码 使用gin 框架

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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 框架

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# -*- 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 的压测结果

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

python 的压测结果

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

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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
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)

压测结果为

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

好低呀

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

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

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

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

多进程的结果

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

和单线程结果差不多

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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 中最好的

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

做张表来对比一下

框架 说明 Requests/sec Transfer/sec
flask 单线程 368 64kb
flask 多线程 493 86kb
flask gevent 协程 864 104kb
sanic 单workers与多workers 数据差不多 2737 291kb
gin 4967 664kb

总结

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