原文首发于 kuricat.com

Golang webApp

压测的目的

在讲 压测的指标之前,我们需要先明确一下压测的目的, 压测不是压的出最高的 QPS 就 OK …… 我们还需要关注 Web App 在 各种压力下, 对应的 指标表现 . 同时, 利用压测找出 App 或 框架 最适合的 压力范围 也是 非常重要的报告内容. 基于这些 压测数据 和 报告, 我们就能更好的 选型 和 资源调配.

指标

CPUMEMHTTP 状态码Timeout 状态响应时延网卡流量

QPS

这个指标 没啥好说的, 就是 每秒处理请求数, 属于客观指标, 对于 Go Web 程序来讲, 主要影响 QPS 的因素在于 CPU 和 bandwidth.

CPU && 响应时延 && Timeout

CPU响应时延Timeout
Go Web App

当我们把 并发数加到 100 并发时, 我们会发现 QPS 不会继续上升, 仍然保持 10k 上下, 但响应时延会上升, 例如 20ms 左右

我们接着把 并发量往上加, 加到 1000 并发的时候, 我们发现 QPS 仍然在 10k 上下, 但响应时延已经疯涨到 300ms 甚至部分请求的响应时延已经上涨到 1s , 并伴随 少量 请求 Timeout.

实验结论, 对于 GoWeb App , 随着并发数的上涨, 我们的 QPS 也不断上涨,而响应时延不会跟随上涨, 直到到达 该程序的 最合适的压力范围. 在达到最合适的压力范围后, 继续提升 并发量, QPS 不会接着上涨, 而 响应时延会逐步上涨, 在最后极端状况下, 可能会伴随 请求 Timeout.

那么为什么会这样呢? 我们需要简单了解一下 Golang Http Server 的运行模型.

net/http

你可以看着下面这个图, 由于计算力有限, CPU 的计算力就相当于一个 固定大小的窗口, CPU 的计算力窗口 在 由 Goroutine 组成的长条上滑动来完成计算范围内的 Goroutine 计算任务.

当 Groutine 的长条 过长, 导致长条后面的 Goroutine 太久没有被分到时间片计算, 这将导致 Client 端直接 Timeout , 这也就能讲通上面为什么 并发量 持续提升, 而 QPS 不变, 请求时延 和 Timeout 请求量 跟随并发量持续提升.

MEM

通常 Goweb 程序 在运行过程中占用的 mem 是基本不会有太大或者太急速的上涨 (特殊情况除外), 如果发现 mem 占用 在 压测过程中持续上涨, 建议 用 pprof debug 一下是不是有哪里 内存泄漏了…… pprof 的 guide 请点这里

网卡流量

上行/下行流量/包

HTTP 状态码

在压测过程中,我们碰到最多的异常状态码通常是 499 和 5xx 系列,

  • 499
    • 客户端取消
  • 5xx
    • 500 服务端程序报错
    • 503 按照定义是 服务过载的意思, 但是 根据通常的 Goweb 的运行模型 , 在过载的时候报的是 Timeout 而不是 503 状态码, 但这样是不正确的反馈, 通常会修改 Goweb 的 Goroutine Pool 或者 在链路做 QOS
    • 502 && 504 或许是 网关出错

压测工具

最后提一下压测工具, 个人用的比较多的是 wrk, 不过目前的 压测工具基本类似, 都是指定 并发数, 和压测时间, 来获取到 QPS.

不过也有另一种 压测工具, 可以自己逐步推高 并发数, 在 并发数停滞 或者 响应时延 开始提升 或者 出现 Timeout 的时候停下来.