V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
herozem
V2EX  ›  分享创造

经过再次重写之后,性能已经是 Nginx 的 1.76 倍

  •  
  •   herozem · 2018-01-25 16:12:47 +08:00 · 18289 次点击
    这是一个创建于 2255 天前的主题,其中的信息可能已经有所发展或是发生改变。

    guard 是一个高性能熔断器+代理

    RT。上周末发了 https://www.v2ex.com/t/424778#reply14

    然后最近几天一直在迭代。从把锁丢掉,到重写 radix tree 和融合统计模块,到把 net/http 换成 fasthttp, 在我的笔记本上,QPS 从 7k 左右到了 22k,对比 Nginx,性能从 Nginx 的 0.55 到 1.76 倍。改写的过程非常 累,因为写好的测试需要一遍一遍推翻重写。数据结构也需要仔细设计。hot-path 需要非常注意避免性能问题。

    但是改写的过程也学到了很多东西,例如怎么去优化 Go 的性能,期间尝试了 prefork 模式,减少锁,缩小临界区,使用 CAS,调整 GOGC参数 等等等等。

    项目链接:

    https://github.com/jiajunhuang/guard

    核心模块的设计基本上已经稳定了,接下来要做的事情是:

    • 改写 time.Now,使用自己维护的粗略时间以减少性能损耗
    • 将 radix tree 中的 path 和索引改为[]byte,以便进一步降低 string 和[]byte 之间转换的损耗
    • 嵌入 viper,以便提供动态配置更改的功能
    • graceful restart

    欢迎大家关注和 star,以及提出意见,建议和 PR !

    第 1 条附言  ·  2018-01-26 10:16:29 +08:00
    做了一下 Benchmark,不知道这样可以不。如果有不对的地方,麻烦指正出来然后可以再测。

    https://github.com/jiajunhuang/guard/blob/master/BENCHMARK.md
    第 2 条附言  ·  2018-01-26 19:44:03 +08:00
    要说明的是,guard 最重要的功能,也是最初的目的,是熔断,做一个通用型熔断器。性能只是其中一个重要的功能,和 Nginx 对比,只是为了做对比,并不是要说“这个这个做的比 Nginx 更好”,我没有这个兴趣。

    Nginx 是我最喜欢的软件之一。
    45 条回复    2018-03-30 12:21:15 +08:00
    LuckCode
        1
    LuckCode  
       2018-01-25 16:35:54 +08:00 via iPhone
    不懂 bd,star 学习
    xwhxbg
        2
    xwhxbg  
       2018-01-25 16:41:56 +08:00
    厉害了,不知道有木有跟 traefik 对比一下性能,另外 string 和 byte 不是应该有相同的 underlying value 吗?这俩之间转换有什么特别大的消耗呀?
    herozem
        3
    herozem  
    OP
       2018-01-25 16:48:43 +08:00
    @LuckCode star 也是贡献~~鼓励开发者哈哈

    @xwhxbg 暂时还没有和 trafik 对比,trafik 说 [自己是 Nginx 的 85%]( https://docs.traefik.io/benchmarks/) 左右,起初我也是以这个为目标的。[]byte 是 mutable 的, string 是 immutable 的,如果底层共享的话,会发大乱子的。

    参考: https://syslog.ravelin.com/byte-vs-string-in-go-d645b67ca7ff
    xwhxbg
        4
    xwhxbg  
       2018-01-25 16:54:51 +08:00
    @herozem 学习了,厉害厉害,那已经是 traefik 的两倍了,看了下配置方式很新颖啊,是不是应该以后会有一个界面看 access log 和 register app
    herozem
        5
    herozem  
    OP
       2018-01-25 16:59:53 +08:00
    @xwhxbg 讲真,重写之后原来的配置方式被删掉了 :joy: 之后的话这些功能应该都可以慢慢加上的。不过当务之急是完善核心功能+继续压榨 Go 的性能。

    之前是有一个接口返回 app 和对应的统计数据的,不过如上所说,暂时被删掉了 :joy:

    欢迎贡献代码
    ryd994
        6
    ryd994  
       2018-01-25 17:24:50 +08:00 via Android
    希望能看到具体的测试场景和 nginx 配置,否则单凭 req/s 说服力不大
    xwhxbg
        7
    xwhxbg  
       2018-01-25 17:25:49 +08:00
    @herozem 我看了下代码,位运算好销魂。。。表示理解不能,超出了我的能力范围,贡献代码怕是不可能了
    bramblex
        8
    bramblex  
       2018-01-25 17:28:10 +08:00   ❤️ 2
    讲真 go 这种带着 runtime 的语言是怎么超越 nginx 那种 *****ptr 的优化的。nginx 在很多方面都已经是性能压榨的极限了。
    zhs227
        9
    zhs227  
       2018-01-25 17:31:55 +08:00
    初学者,怀着敬畏点评一下
    fasthttp 的头部都是在使用的时候才解析,之前都是一堆的 byte 地址传来传去。如果要逐个解析头部字段,速度就会明显慢下来。
    另外,fasthttp 前端目前不支持 http2。后面做一些 web 选型就没有再考虑这个。
    不过基于 traefik 的 benchmark 来看,大概 80%相当于 nginx 是靠谱的
    herozem
        10
    herozem  
    OP
       2018-01-25 17:35:13 +08:00
    @bramblex 尽可能的重复利用已经申请的对象,尽可能的避免锁。pprof,哪里耗时打哪里

    @xwhxbg 目前还在开发,变更比较大,等稳定了,注释多了就好读了 :)

    @ryd994 目前还只有简单的测试,等稳定了之后会把 benchmark 做的更详细更专业的 :) 目前是:Nginx 4 worker 返回默认的 index.html。Nginx 4 worker 监听 9999 做代理,guard 监听 23456 做代理。max fd 都是设置 2048000。

    因为都只能在自己的笔记本上跑跑,互相影响是肯定的,之后如果有机会的话,可以用强大的机器+虚拟机的模式来跑分
    herozem
        11
    herozem  
    OP
       2018-01-25 17:39:09 +08:00
    @zhs227 是的,原本我计划自己实现 proxy 也是这种思路。因为作为 proxy server,并不需要读 body。另外 net/http 实现里申请了太多的内存,相当的影响性能。 另外 guard 暂时也不会支持 http2。http/1.x 比 2 的工具等
    都成熟很多。更专业的 benchmark 我会在把功能做成熟做稳定之后再来,目前还只是一个粗略的跑分
    caola
        12
    caola  
       2018-01-25 17:50:49 +08:00
    不支持最新的 QUIC 就一切免谈,
    之前用 caddy 的 quic,最高只支持到 quic 38 版本,跟不上时代了
    果断放弃,改投 trafficserver 大法,可以支持到 quic 41 版本,爽。。
    zhujinliang
        13
    zhujinliang  
       2018-01-25 18:22:47 +08:00
    time.Now 这块能详细说一下么
    如何取当前时间的,精确度有多少?
    我想弄一个能精确到 1ms 或 0.5ms 的,但 linux 时间片好像就需要 10ms
    gouchaoer
        14
    gouchaoer  
       2018-01-25 18:25:10 +08:00 via Android
    你能跑个火焰图和 nginx 火焰图说明一下你哪里更厉害么?
    herozem
        15
    herozem  
    OP
       2018-01-25 18:56:28 +08:00
    est
        16
    est  
       2018-01-25 19:54:01 +08:00 via Android
    不太可能超过 nginx 贴配置看看? nginx worker 数量要等于 GOMAXPROC 才公平?
    miketeam
        17
    miketeam  
       2018-01-25 21:26:11 +08:00 via iPhone
    楼主,我搭你的车来了,已给 star
    cholerae
        18
    cholerae  
       2018-01-26 00:28:11 +08:00 via Android
    @zhujinliang 带 runtime 和调度器的语言就要求别太高了,1ms 的精度基本不可能的
    cholerae
        19
    cholerae  
       2018-01-26 00:32:07 +08:00 via Android
    @herozem 那位朋友想要高精度时钟,这个 coarseTime 精度应该比 time.Now 还低啊
    herozem
        20
    herozem  
    OP
       2018-01-26 00:42:54 +08:00 via iPad
    @cholerae 应该还是可以的,有写一个 benchmark,发现 time.Now 损耗大概在 40ns 左右,用 coarse 实现应该是可以控制到这位朋友要的精度的。毕竟 CPU 非常的快
    sagaxu
        21
    sagaxu  
       2018-01-26 01:56:55 +08:00
    能公布测试的细节吗? i5-3210M 是双核不是四核,nginx cpu affinity 怎么设置的?


    @herozem GC 卡顿都不敢保证 < 1ms,只能实现软高精度时间
    assad
        22
    assad  
       2018-01-26 07:38:45 +08:00 via Android
    要是能取代 nginx 就牛逼了
    herozem
        23
    herozem  
    OP
       2018-01-26 07:56:22 +08:00 via iPad
    @gouchaoer
    @est
    @sagaxu
    @assad 开发的时候都是用 wrk 粗略压测,CPU 是物理双核虚拟四核。GOMAXPROCS 默认是虚拟核心数(我 1.9 ),Nginx worker 是 4。我今天好好的做一次专业一些的压测,请大家耐心等候。
    herozem
        24
    herozem  
    OP
       2018-01-26 08:01:01 +08:00 via iPad
    @assad 没准备取代,设计的时候就没想过要取代。理想的方式是躲在 Nginx 之后作为一个中间件。因为 Nginx 非常的成熟非常的专业,第二 fasthttp 没有完全遵循 HTTP RFC,第三 guard 并不准备增加 Nginx 那么多可更改的配置和“脚本支持”。

    理想的方式是 Nginx 处理完规则,然后打到 guard,然后后面是应用
    sagaxu
        25
    sagaxu  
       2018-01-26 09:43:57 +08:00 via Android
    @herozem nginx 后面的中间件生存空间有限,一不留神就给 nginx 补丁或者模块取代了
    herozem
        26
    herozem  
    OP
       2018-01-26 10:18:52 +08:00
    @sagaxu :joy: 那可能是命数。。。要替代 Nginx,有太多太多的功能要开发和完善。其实这个项目起初也是为内网的一个需求设计的,不过目前还不够成熟所以还没有上生产。所以设计之初就是躲在 Nginx 后面工作的 (地下工作者
    bestkayle
        27
    bestkayle  
       2018-01-26 10:20:11 +08:00 via Android
    跟 caddy 一样吗
    herozem
        28
    herozem  
    OP
       2018-01-26 10:31:52 +08:00
    @bestkayle 我们不一样 :)
    VYSE
        29
    VYSE  
       2018-01-26 11:40:28 +08:00
    @zhujinliang #13 如果只是对 cpu 运转时间做比较操作,利用 time.Now()的 monotonic clock reading 精度是最高, 读的来源实际就是 cpu 开机至今的 cycle 数, 某些打印操作会导致读自然时间,这就依赖系统时间 api 误差(内核其实只是周期性 update 时间戳)
    zhicheng
        30
    zhicheng  
       2018-01-26 15:37:03 +08:00   ❤️ 8
    一般用非 C/C++ 写的基础软的生命周期:
    1, 写了个 demo 发现性能能接近 nginx,觉得有戏。
    2, 经过一番优化,性能已经完全超过 nginx,觉得非常有戏。
    3, 随着功能的完善,性能逐渐下降,完全无法匹敌 nginx。
    4, 继续经过一翻优化,性能勉强达到 nginx 水平,但代码已经不像高级语言,后悔没有直接用 C。
    5, 用 C 重写了一个 lib,给上层语言调用,性能很好,但 Bug 很多,很容易 Crash。
    6, 发现有人写了个 nginx 插件,很好用,项目被抛弃。
    feverzsj
        31
    feverzsj  
       2018-01-26 15:39:36 +08:00
    这是再测 fasthttp 吧
    herozem
        32
    herozem  
    OP
       2018-01-26 15:52:36 +08:00
    @feverzsj 绝大部分性能确实来自 fasthttp,但是也离不开其他组件的支持。


    @zhicheng 并不想取代 Nginx。在此只是压测时做对比和参照。Nginx 作为我最喜欢的软件之一,怎么能取代呢
    zhicheng
        33
    zhicheng  
       2018-01-26 16:17:21 +08:00
    @herozem
    并不是说要取代 nginx,而是不要把重点放在性能优化上,把重点放到需求上,性能只是一个功能。如果 “非性能” 部分的需求巨大,到时候哪怕再用 C 写一个,也不是问题。
    m939594960
        34
    m939594960  
       2018-01-26 16:25:58 +08:00
    benchmark nginx 的配置没关 log 啊
    gstqc
        35
    gstqc  
       2018-01-26 19:32:30 +08:00   ❤️ 1
    我优化一下 nginx 对比一下
    nginx 单进程,CPU 是虚拟机的 i5-4590 CPU @ 3.30GHz

    测试结果:
    1. 请求 nginx 默认首页
    wrk --latency -c 2048 -d 30 -t 2 http://127.0.0.1:32645/index.html

    Running 30s test @ http://127.0.0.1:32645/index.html
    2 threads and 2048 connections
    Thread Stats Avg Stdev Max +/- Stdev
    Latency 16.54ms 91.31ms 1.97s 96.19%
    Req/Sec 35.86k 14.09k 81.79k 66.50%
    Latency Distribution
    50% 1.55ms
    75% 2.86ms
    90% 4.47ms
    99% 406.41ms
    2140722 requests in 30.08s, 1.69GB read
    Socket errors: connect 1029, read 98, write 0, timeout 564
    Requests/sec: 71172.27
    Transfer/sec: 57.69MB

    2. 请求 nginx 直接 return 的 hello world
    wrk --latency -c 1000 -d 30 -t 2 http://127.0.0.1:32645/hello

    Running 30s test @ http://127.0.0.1:32645/hello
    2 threads and 1000 connections
    Thread Stats Avg Stdev Max +/- Stdev
    Latency 43.54ms 182.36ms 1.83s 94.93%
    Req/Sec 83.45k 12.45k 128.69k 74.58%
    Latency Distribution
    50% 0.99ms
    75% 1.75ms
    90% 50.98ms
    99% 1.12s
    4975705 requests in 30.04s, 749.51MB read
    Socket errors: connect 0, read 21, write 0, timeout 352
    Requests/sec: 165662.55
    Transfer/sec: 24.95MB


    测试配置和脚本见 github:
    https://gist.github.com/travislee8964/76e57bf013923eb9efe9f5cc6c5f9ce9
    gstqc
        36
    gstqc  
       2018-01-26 19:35:57 +08:00
    网络和内核没经过优化,所以有一些 socket errors
    优化后性能应该还能有提升
    0915240
        37
    0915240  
       2018-01-26 19:40:08 +08:00
    强烈关注。。
    herozem
        38
    herozem  
    OP
       2018-01-26 19:40:10 +08:00
    @zhicheng 嗯,这也是一种观点。过早优化是万恶之源没错,但是性能也是一个重要的功能。软件开发的过程并非要剑走偏锋,例如追着性能不放,而是在权衡之下取一个比较好的点。目前这边也没有完全追逐性能
    herozem
        39
    herozem  
    OP
       2018-01-26 19:41:09 +08:00
    @gstqc 如果可以的话,最好能把 guard 的测试一起贴上去。这样方便对比,哈哈~
    wzy44944
        40
    wzy44944  
       2018-01-26 23:05:03 +08:00
    压完看下火焰图更准确些,不然很容易被一些无所谓的配置掩盖掉,比如 nginx 的 accept 锁要去掉,reuseport 打开,而且随机请求压才有意义,只压一个请求会有内存缓存,这点和配置也有关系。既然不是替代 nginx 的,就不要和 nginx 对比了吧,和做同一个业务的软件对比才好
    sammo
        41
    sammo  
       2018-01-28 11:22:55 +08:00 via Android
    终于在 v 站上看到一个有性能追求的软件了
    xxdd
        42
    xxdd  
       2018-01-28 21:37:04 +08:00
    已 star
    从不吝啬 star QAQ
    xman99
        44
    xman99  
       2018-01-31 14:30:13 +08:00
    Mark, 膜拜中
    holulu
        45
    holulu  
       2018-03-30 12:21:15 +08:00
    @caola ATS 试试网上查到的配置,但开不了 QUIC。服务器能正常启动,日志也能看到 QUIC,但没见 UDP 端口有监听啊。
    Chrome stable 和 canary 也只支持 QUIC-39,QUIC-41 好像没有太大意义。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5255 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 37ms · UTC 09:15 · PVG 17:15 · LAX 02:15 · JFK 05:15
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.