首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
宝塔
V2EX  ›  程序员

主流 go-web 服务端框架性能测试

  •  1
     
  •   wslzy007 · 29 天前 · 495 次点击

    测试目的

    由于 K8s 缘故涉猎 go 语言,发现 golang 的 web 框架很多,都号称高性能标杆;之前一直致力于 c++高性能服务端框架研究,出于好奇,想单从性能层面客观比较一下 go 的众多 web 框架,另一方面也希望看看 c++的实现与 go 语言实现之间究竟存在多大差异。

    高性能服务框架评估指标很多,但一般来讲吞吐量与 QPS 是关键考量指标,吞吐量衡量带宽利用率,QPS 主要考验框架调度性能(几乎所有可称之为“高性能”的服务框架都没有吞吐量问题,毕竟网络瓶颈很轻易就达到了)。由于是框架本身 QPS 测试,为了屏蔽 http 协议实现差异选择最精简的协议头(协议处理一般不会有锁,为 cpu 密集型),因此要求请求 /返回报文尽可能小,本文测试基于 http 协议,返回空报文。

    为了实现测试的第二个目的,特将一个自撸的 c++服务框架作为 c++实现的”砖头”,加入到对比测试中。此框架尚未开源,其高性能特性保障体现在如下设计上:

    • 跨平台实现 socket 多路复用,支持:poll、epoll、kqueue、port、select、IOCP 等模型
    • 采用 lock-free 算法
    • 线程池设计
    • socket 连接池
    • 多级任务队列 ……

    PS: 好吧,这样一来貌似更接近测试 socket 服务框架调度性能…… 不要纠结 keep-alive,因为 wrk 使用 HTTP/1.1,默认都是 keep-alive 的

    测试环境

    env.jpg

    环境设置

    ulimit -n 2000

    压测工具

    wrk 由于环境限制,只能 wrk 客户端和待测试服务端在一台机器上运行

    c++自研框架

    • 启动脚本:(最大 2000 个并发连接,2 个线程处理,http 端口 8080) ./proxy_server -i2000 -o2000 -w2 -x8080
    • 如有条件测试 linux 系统可自行下载服务端(选择对应平台的包): https://github.com/lazy-luo/smarGate
    • http 返回报文:
    $ curl -i http://localhost:8080/
    HTTP/1.1 200 OK
    Content-Length: 0
    Connection: keep-alive
    
    • 压测结果:
    $wrk -d 100s -c 1024 -t 8 http://localhost:8080/
    Running 2m test @ http://localhost:8080/
      8 threads and 1024 connections
      Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency    13.03ms    3.80ms 100.73ms   86.97%
        Req/Sec     9.43k     1.64k   39.35k    88.23%
      7509655 requests in 1.67m, 444.03MB read
      Socket errors: connect 0, read 794, write 2, timeout 0
    Requests/sec:  75018.11
    Transfer/sec:      4.44MB
    
    • 资源占用: proxy_server.jpg

    go-restful 框架:

    • main_go-restful.go
    package main
    import (
       "github.com/emicklei/go-restful"
       "net/http"
    )
    func main(){
    	ws := new(restful.WebService)
    	ws.Route(ws.GET("/").To(hello))
    	restful.Add(ws)
    	http.ListenAndServe(":8080",nil)
    }
    func hello(req *restful.Request,resp *restful.Response){
    	resp.Write([]byte(""))
    }
    
    • http 返回报文:
    $curl -i http://localhost:8080/
    HTTP/1.1 200 OK
    Date: Mon, 21 Oct 2019 03:54:27 GMT
    Content-Length: 0
    
    • 压测结果:
    $wrk -d 100s -c 1024 -t 8 http://localhost:8080/
    Running 2m test @ http://localhost:8080/
      8 threads and 1024 connections
      Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency    19.72ms   10.57ms 331.94ms   87.67%
        Req/Sec     6.52k     1.24k   23.75k    80.42%
      5180908 requests in 1.67m, 370.57MB read
      Socket errors: connect 0, read 844, write 3, timeout 0
    Requests/sec:  51757.61
    Transfer/sec:      3.70MB
    
    • 资源占用: go-restful.jpg

    go-echo 框架:

    • main_go-echo.go
    package main
    import (
    	"net/http"
    	"github.com/labstack/echo"
    )
    func main() {
    	e := echo.New()
    	e.GET("/", func(c echo.Context) error {
    		return c.String( http.StatusOK, "")
    	})
    	e.Logger.Fatal(e.Start(":8080"))
    }
    
    • http 返回报文:
    $ curl -i http://localhost:8080/
    HTTP/1.1 200 OK
    Content-Type: text/plain; charset=UTF-8
    Date: Mon, 21 Oct 2019 04:09:24 GMT
    Content-Length: 0
    
    • 压测结果:
    $ wrk -d 100s -c 1024 -t 8 http://localhost:8080/
    Running 2m test @ http://localhost:8080/
      8 threads and 1024 connections
      Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency    17.32ms    8.19ms 252.60ms   90.70%
        Req/Sec     7.52k     1.35k   39.96k    80.55%
      5974370 requests in 1.67m, 660.92MB read
      Socket errors: connect 0, read 431, write 67, timeout 0
    Requests/sec:  59686.09
    Transfer/sec:      6.60MB
    
    • 资源占用: go-echo.jpg

    go-iris 框架:

    • main_go-iris.go
    package main
    import(
    	"time"
    	"github.com/kataras/iris"
    	"github.com/kataras/iris/cache"
    )
    func main(){
    	app := iris.New()
    	app.Logger().SetLevel("error")
    	app.Get("/",cache.Handler(10*time.Second),writeMarkdown)
    	app.Run(iris.Addr(":8080"))
    }
    func writeMarkdown(ctx iris.Context){
    	ctx.Markdown([]byte(""))
    }
    
    • http 返回报文:
    $ curl -i http://localhost:8080/
    HTTP/1.1 200 OK
    Content-Type: text/html; charset=UTF-8
    Date: Mon, 21 Oct 2019 04:11:59 GMT
    Content-Length: 0
    
    • 压测结果:
    $ wrk -d 100s -c 1024 -t 8 http://localhost:8080/
    Running 2m test @ http://localhost:8080/
      8 threads and 1024 connections
      Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency    22.03ms    7.99ms 140.47ms   84.58%
        Req/Sec     5.79k   775.23    19.31k    80.35%
      4608572 requests in 1.67m, 505.43MB read
      Socket errors: connect 0, read 726, write 22, timeout 0
    Requests/sec:  46041.23
    Transfer/sec:      5.05MB
    
    • 资源占用: go-iris.jpg

    go-gin 框架

    • main_go-gin.go
    package main
    import (
        "fmt"
        "net/http"
        "log"
        "github.com/julienschmidt/httprouter"
    )
    func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
        fmt.Fprint(w, "")
    }
    func main() {
        router := httprouter.New()
        router.GET("/", Index)
        log.Fatal( http.ListenAndServe(":8080", router))
    }
    
    • http 返回报文:
    $ curl -i http://localhost:8080/
    HTTP/1.1 200 OK
    Date: Mon, 21 Oct 2019 04:15:33 GMT
    Content-Length: 0
    
    • 压测结果:
    $ wrk -d 100s -c 1024 -t 8 http://localhost:8080/
    Running 2m test @ http://localhost:8080/
      8 threads and 1024 connections
      Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency    16.71ms    7.72ms 268.45ms   87.79%
        Req/Sec     7.71k     1.58k   21.27k    82.12%
      6130281 requests in 1.67m, 438.47MB read
      Socket errors: connect 0, read 693, write 36, timeout 0
    Requests/sec:  61243.74
    Transfer/sec:      4.38MB
    
    • 资源占用: go-gin.jpg

    go-chi 框架:

    • main_go-chi.go
    package main
    import (
        "net/http"
        "github.com/go-chi/chi"
    )
    func main() {
        r := chi.NewRouter()
        r.Get("/", func(w http.ResponseWriter, r *http.Request) {
            w.Write([]byte(""))
        })
        http.ListenAndServe(":8080", r)
    }
    
    • http 返回报文:
    $ curl -i http://localhost:8080/
    HTTP/1.1 200 OK
    Date: Mon, 21 Oct 2019 04:18:42 GMT
    Content-Length: 0
    
    • 压测结果:
    $ wrk -d 100s -c 1024 -t 8 http://localhost:8080/
    Running 2m test @ http://localhost:8080/
      8 threads and 1024 connections
      Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency    17.17ms    8.47ms 253.47ms   90.07%
        Req/Sec     7.65k     1.42k   26.08k    79.76%
      6071695 requests in 1.67m, 434.28MB read
      Socket errors: connect 0, read 110, write 2, timeout 0
    Requests/sec:  60658.49
    Transfer/sec:      4.34MB
    
    • 资源占用: go-chi.jpg

    结论:

    | - | cpu-free | mem-usage | qps | | --------- | ------- | -------- | ------- | | c++ | 15%-20% | 6M | 75018.11 | | go-gin | 0%-1.5% | 28M | 61243.74 | | go-chi | 0%-1% | 28M | 60658.49 | | go-echo | 0%-0.5% | 28M | 59686.09 | | go-restful | 0%-0.5% | 34M | 51757.61 | | go-iris | 0%-1% | 37M | 46041.23 |

    • go 语言 web 框架中 gin、chi、echo 性能相当,gin 略显优势,iris 实测效果不佳;
    • go 语言与 c++语言网络框架比较还是存在一定性能差距,但不是决定性的;
    • go 语言整体资源耗用大,c++足够轻量高效;
    • go 语言真的很易用且简洁!!就是三方依赖太多 ,拿来主义,问题排查那是相当酸爽...... 当然,随着依赖包升级你会一直爽 :)
        1
    linnchord   29 天前
    go 开发 web 已经够酸爽了,还 C++
        2
    aliipay   29 天前
    一直用 net/http,感觉很爽
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1275 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 24ms · UTC 17:39 · PVG 01:39 · LAX 09:39 · JFK 12:39
    ♥ Do have faith in what you're doing.