我尝试比较了Go语言的web框架Echo和Python的web框架Flask的速度

我們來比較一下 Golang 的 Echo 和 Python 的 Flask 的執行速度。

我之前一直在使用Python,没有特别的不满意,但最近开始学习使用Golang。尽管Python在开发一些小型项目上很方便,但当需要速度时,我想用Golang来感受一下它能有多快。所以我决定比较Golang中代表性的web框架Echo和Python中的轻量级web框架Flask,看看它们在速度上有多大的差异。

考试环境

在VMware上建立的Ubuntu 16.04是使用普通硬盘而非SSD。
配置为i7处理器,内存6GB。
安装了Python 2.7和Go语言1.6。

返回一个简单的字符串的Web应用程序

为了掌握直观感受,首先制作一个简单的网络应用程序。

使用Golang的Echo库创建一个简单的应用程序。

首先,从一个使用Go语言创建的简单Web应用开始。


package main
import (
        "net/http"
        "github.com/labstack/echo"
        "github.com/labstack/echo/engine/standard"
)
func main() {
        e := echo.New()
        e.GET("/", func(c echo.Context) error {
                return c.String(http.StatusOK, "Hello, World!")
        })
        e.Run(standard.New(":1323"))
}

如果使用Golang的Echo库,只需写入以下内容,就可以快速创建一个返回”Hello World”的web应用程序,非常方便。

试着测量golang-echo的速度。

用Apache Bench工具来测试速度

shibacow@ubuntu:~/prog/golang/echo_test$ ab -n 100000 -c 100 http://localhost:1323/
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 10000 requests
...
Completed 100000 requests
Finished 100000 requests

Server Software:
Server Hostname:        localhost
Server Port:            1323

Document Path:          /
Document Length:        13 bytes

Concurrency Level:      100
Time taken for tests:   9.525 seconds
Complete requests:      100000
Failed requests:        0
Total transferred:      13000000 bytes
HTML transferred:       1300000 bytes
Requests per second:    10498.93 [#/sec] (mean)
Time per request:       9.525 [ms] (mean)
Time per request:       0.095 [ms] (mean, across all concurrent requests)
Transfer rate:          1332.87 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    3   2.8      2      19
Processing:     0    6   3.2      7      31
Waiting:        0    5   2.5      4      26
Total:          0    9   4.1      9      33

Percentage of the requests served within a certain time (ms)
  50%      9
  66%     11
  75%     12
  80%     13
  90%     15
  95%     17
  98%     18
  99%     19
100% 33 (longest request)

每秒请求次数:10498.93次/秒

不需要进行调整,似乎可以达到每秒1万个请求。

使用Python-Flask创建的简单应用程序。

下一步,我尝试研究一下Flask。

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

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

使用 Flask 搭建一个简单的网页应用,并返回一个”Hello World”的字符串。

Python-Flask的性能测试

shibacow@ubuntu:~/prog/golang/echo_test$ ab -n 10000 -c 100  http://localhost:5000/
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 1000 requests
...
Completed 10000 requests
Finished 10000 requests


Server Software:        Werkzeug/0.11.10
Server Hostname:        localhost
Server Port:            5000

Document Path:          /
Document Length:        12 bytes

Concurrency Level:      100
Time taken for tests:   8.190 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      1680000 bytes
HTML transferred:       120000 bytes
Requests per second:    1220.97 [#/sec] (mean)
Time per request:       81.902 [ms] (mean)
Time per request:       0.819 [ms] (mean, across all concurrent requests)
Transfer rate:          200.32 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.7      0       8
Processing:     2   81  12.6     81     148
Waiting:        1   81  12.5     81     148
Total:          6   81  12.2     81     148

Percentage of the requests served within a certain time (ms)
  50%     81
  66%     90
  75%     91
  80%     92
  90%     95
  95%     98
  98%    101
  99%    106
 100%    148 (longest request)

每秒请求次数:1220.97 次/秒

不做任何特别处理, 大约能达到1220req/sec。

连接到Mongo并搜索并返回结果的Web应用程序。

因为简单的应用程序与现实问题存在不一致,所以连接到mongo并进行搜索以返回结果,加入了一些超出现实的元素。
参考了该网站。

使用golang-echo连接到mongo的网络应用程序


package main

import (
        // サーバ系
        "net/http"
        "github.com/labstack/echo"
        "github.com/labstack/echo/engine/standard"

        // mongo系
        "fmt"
        "gopkg.in/mgo.v2"
        "gopkg.in/mgo.v2/bson"

        // 型
        "strconv"
        //"reflect"
)

// ユーザクラス
type User struct {
        Name  string `bson:"name"`
        Id int `bson:"id"`
}

func main() {
        e := echo.New()
        e.GET("/", func(c echo.Context) error {
                return c.String(http.StatusOK, "Hello, World!")
        })

        // :id, :name に値が入っていないとNot Foundになる
        e.GET("/users/id/:id", func(c echo.Context) error {

                // 数値に変換する必要あり
                var id int
                id, _ = strconv.Atoi(c.Param("id"))

                //idでDBを引く
                session, _ := mgo.Dial("mongodb://localhost")
                defer session.Close()
                db := session.DB("test")

                // id指定でユーザ取得
                var results []User
                fmt.Println(id)
                db.C("user").Find(bson.M{"id": id}).All(&results)

                fmt.Println("Results of one user: ", results)
                fmt.Println(len(results))

                if len(results) == 0 {
                        return c.String(http.StatusOK, "No user")
                }else{
                        name := results[0].Name
                        return c.String(http.StatusOK, "Hello, " + name)
                }
        })

        e.POST("/user", func(c echo.Context) error {
                return c.String(http.StatusOK, "Hello, POST user")
        })

        // ポート
        e.Run(standard.New(":1323"))
}

Golang-Echo和Mongo连接的基准测试

与Mongo建立连接并返回结果的基准测试。

shibacow@ubuntu:~/prog/golang/echo_test$ ab -n 10000 -c 100  http://localhost:1323/users/id/1
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 1000 requests
.....
Completed 10000 requests
Finished 10000 requests

Server Software:
Server Hostname:        localhost
Server Port:            1323

Document Path:          /users/id/1
Document Length:        11 bytes

Concurrency Level:      100
Time taken for tests:   9.156 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      1280000 bytes
HTML transferred:       110000 bytes
Requests per second:    1092.21 [#/sec] (mean)
Time per request:       91.557 [ms] (mean)
Time per request:       0.916 [ms] (mean, across all concurrent requests)
Transfer rate:          136.53 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   4.4      0      29
Processing:     9   90  24.9     88     213
Waiting:        9   89  25.1     87     213
Total:         25   91  24.3     89     213

Percentage of the requests served within a certain time (ms)
  50%     89
  66%    100
  75%    107
  80%    112
  90%    123
  95%    134
  98%    147
  99%    156
 100%    213 (longest request)

每秒请求次数:1092.21次/秒

使用Python-Flask连接Mongo数据库并获取结果。

制作一个使用MongoDB进行连接的应用程序。


#!/usr/bin/env python
# -*- coding:utf-8 -*-

from flask import Flask
from flask_pymongo import PyMongo
app = Flask(__name__)
app.config['MONGO_HOST']='localhost'
app.config['MONGO_DBNAME'] = 'test'
mongo = PyMongo(app)

@app.route("/users/id/<int:id>")
def user_id(id):
    user= mongo.db.user.find_one({"id":id})
    msg="Hello id={} name={}".format(user["id"],user['name'])
    return msg

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == '__main__':
    app.run(host="0.0.0.0",debug=False)

使用Python Flask进行基准测试。

shibacow@ubuntu:~/prog/python/flask_test$ ab -n 10000 -c100 http://localhost:500
0/users/id/1
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 1000 requests
.....
Completed 10000 requests
Finished 10000 requests

Server Software:        Werkzeug/0.11.10
Server Hostname:        localhost
Server Port:            5000

Document Path:          /users/id/1
Document Length:        20 bytes

Concurrency Level:      100
Time taken for tests:   12.639 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      1760000 bytes
HTML transferred:       200000 bytes
Requests per second:    791.22 [#/sec] (mean)
Time per request:       126.387 [ms] (mean)
Time per request:       1.264 [ms] (mean, across all concurrent requests)
Transfer rate:          135.99 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.7      0       8
Processing:     6  126  11.8    125     164
Waiting:        6  125  11.8    125     163
Total:         11  126  11.5    125     164

Percentage of the requests served within a certain time (ms)
  50%    125
  66%    129
  75%    131
  80%    132
  90%    138
  95%    143
  98%    149
  99%    153
 100%    164 (longest request)

每秒请求数:791.22次/秒

整理一下

我比较了Golang的Echo和Python的Flask的速度。

比較シンプルなアプリ複雑なアプリgolang-echo10498 req/sec1092 req/secpython-flask1220 req/sec791 req/sec

在一个简单的应用程序中,性能差异大约是10倍,但是连接到Mongo时速度差异并不明显。每次请求都要连接到Mongo,所以可能负载更高。如果使用echo或pymongo连接池,结果可能会有所改变。我比较Golang和Python并不是想贬低Python的性能,只是想体验一下它们之间大致有多大差距。

总结(追加)

在下面的附注中提到,停止每次连接到Mongo数据库,而是连接一次并进行重用,可以大大提高速度。但是,由于会话保持状态,因此在使用时需要进行验证,以确定在更新或提交等操作时会发生什么,因此在生产环境中使用之前需要进行验证,而不仅仅依靠基准测试。

比較シンプルなアプリ複雑なアプリgolang-echo10498 req/sec1092 req/secgolang-echo(下記改良版)なし6283.74 req/secpython-flask1220 req/sec791 req/sec

使用golang-echo连接mongo并提高获取结果的速度

改进 Golang-Echo 的 Mongo 连接。

在上面的例子中,每个请求都重复连接和断开与mongo数据库的连接。现在改为在GET请求的外部进行连接和断开操作。由于将输出切换到标准输出导致速度变慢,所以我们也取消了这一操作。

package main

import (
        // サーバ系
        "net/http"
        "github.com/labstack/echo"
        "github.com/labstack/echo/engine/standard"

        // mongo系
        //"fmt" //標準出力は遅いので、止める
        "gopkg.in/mgo.v2"
        "gopkg.in/mgo.v2/bson"

        // 型
        "strconv"
        //"reflect"
)

// ユーザクラス
type User struct {
        Name  string `bson:"name"`
        Id int `bson:"id"`
}

func main() {
        e := echo.New()
        //idでDBを引くGETの外側に出して、毎回の接続・切断をやめた
        session, _ := mgo.Dial("mongodb://localhost")
        defer session.Close()
        db := session.DB("test")

        e.GET("/", func(c echo.Context) error {
                return c.String(http.StatusOK, "Hello, World!")
        })

        // :id, :name に値が入っていないとNot Foundになる
        e.GET("/users/id/:id", func(c echo.Context) error {

                // 数値に変換する必要あり
                var id int
                id, _ = strconv.Atoi(c.Param("id"))


                // id指定でユーザ取得
                var results []User
                //fmt.Println(id)
                db.C("user").Find(bson.M{"id": id}).All(&results)

                //fmt.Println("Results of one user: ", results)
                //fmt.Println(len(results))

                if len(results) == 0 {
                        return c.String(http.StatusOK, "No user")
                }else{
                        name := results[0].Name
                        return c.String(http.StatusOK, "Hello, " + name)
                }
        })

        e.POST("/user", func(c echo.Context) error {
                return c.String(http.StatusOK, "Hello, POST user")
        })

        // ポート
        e.Run(standard.New(":1323"))
}

改良版基准测试

经过以上改进,速度显著提升,达到了6282次/秒。

shibacow@ubuntu:~$ ab -n 100000 -c 100 http://localhost:1323/users/id/1
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 10000 requests
....
Completed 100000 requests
Finished 100000 requests


Server Software:
Server Hostname:        localhost
Server Port:            1323

Document Path:          /users/id/1
Document Length:        11 bytes

Concurrency Level:      100
Time taken for tests:   15.914 seconds
Complete requests:      100000
Failed requests:        0
Total transferred:      12800000 bytes
HTML transferred:       1100000 bytes
Requests per second:    6283.74 [#/sec] (mean)
Time per request:       15.914 [ms] (mean)
Time per request:       0.159 [ms] (mean, across all concurrent requests)
Transfer rate:          785.47 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    4   3.2      3      21
Processing:     0   12   5.0     12      44
Waiting:        0   10   4.9     10      44
Total:          0   16   5.2     15      45

Percentage of the requests served within a certain time (ms)
  50%     15
  66%     17
  75%     19
  80%     20
  90%     22
  95%     25
  98%     29
  99%     32
 100%     45 (longest request)

每秒请求数:6283.74个/秒

大大提速了。

广告
将在 10 秒后关闭
bannerAds