零:缘起

最近两个事情

一个是easeprobe,haocl 对其http probe 做了一个10k的benchmark easeprobe benchmark

一个是读到go memory这篇文章 go-memory-ballast

谈到技术,并发是一个绕不过的话题,从第一家公司单机2万qps,到一个50w+ tps的超大型分布式交易系统。

那么自己能否来简单来测试一下呢? 跑一个web server,来个10k 并发,再来个可观测,看下瓶颈到底在哪里? 如何提升? 说干就干。

一:go http server

goroutine的存在,让go在网络编程这块很高效,也有很多优秀的http 框架,比如 gin echo fasthttp

fasthttp 比较快,我们先不读取db和其他,仅让cpu是一个瓶颈,仅写个hello world,收到请求后,返回一行文本。

示例代码

package main

import (
	"fmt"
	"github.com/valyala/fasthttp"
)

// request handler in fasthttp style, i.e. just plain function.
func fastHTTPHandler(ctx *fasthttp.RequestCtx) {
	fmt.Fprintf(ctx, "Hi there! RequestURI is %q", ctx.RequestURI())
}

func main() {
	fasthttp.ListenAndServe(":8087", fastHTTPHandler)
}

二:go http client 压测的client

2.1 压测client选择

  • 简单实现版本,使用time.Sleep 函数,来均匀分布所有的请求到一个server。
  • 复杂实现版本,来一个goroutine的pool,一个http connect的pool,分别控制最大连接数和最大goroutine数目,并且根据请求耗时,动态调整两个pool的大小,然后到一个稳定的状态。
  • 别人现有的,找到一个 go-strees-testing 的项目,可以先用起来

2.2 先选择现有的试试效果

https://github.com/link1st/go-stress-testing

#!/bin/bash

go-stress-testing-linux -c 100 -n 100000 -u http://127.0.0.1:8087

这里原生不支持多核cpu,所以多核的话,我们需要下载下来,修改main函数,把cpu的数量从 runtime.GOMAXPROCS(1) 修改成runtime.GOMAXPROCS(runtime.NumCPU())

三:可观测

3.1 观测内容

观测分为几点,一个是业务监控,一个是系统监控,像我们这种,没有业务逻辑,仅监控系统即可

系统监控,又分为主机系统监控,应用系统监控、中间件、DB

主机监控有cpu、内存、磁盘、网络的监控,我们这里磁盘其实可以忽略掉

应用系统监控,go runtime监控,比如gc、堆栈

DB和中间件,暂时没有,先不写

3.2 观测方案

系统监控,使用shell、linux 命令行即可,手动监控

应用监控,使用prometheus 的golang client 采集,grafana 展示

四:实战

4.1 Mac 实践

问题

server和client 配置好后,开始监测,QPS也就700左右,死活上不去了,内存和cpu都还好,不高,网络链接也不多,但请求的延时就开始超过10s

解决

尽早放弃了,不想折腾,使用linux

4.2 Linux 实践

问题1

这次简单,直接在现有的小鸡上来跑,0.5G内存,1VCPU

结果1

Untitled

Untitled

解决1

可以看到,linux 直接就3800 QPS了,这次是单机CPU满载了,client 64%,server 36%, 100%

这种简单,加机器就可以解决的问题

Untitled

Untitled

解决2

*GOMAXPROCS=4 go run server.go &*
PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
17606 root      20   0 1438212 119252   5284 R 303.7   0.4   5:51.98 client
17485 root      20   0 1527320  15384   9156 S 230.0   0.0   4:29.19 server

30k+的top 和并发数

PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
18814 root      20   0 1816328  53512   9532 S 549.8   0.2   4:21.05 main
17485 root      20   0 1748772  16164   9348 R 166.8   0.0  14:03.47 server
─────┬───────┬───────┬───────┬────────┬────────┬────────┬────────┬────────┬────────┬────────
耗时│ 并发数│ 成功数│ 失败数│   qps  │最长耗时│最短耗时│平均耗时│下载字节│字节每秒│ 状态码 
─────┼───────┼───────┼───────┼────────┼────────┼────────┼────────┼────────┼────────┼────────
1s│    100│  22654│      0│27766.78│   45.56│    0.17│    3.60│1,245,970│1,245,938│200:22654
2s│    100│  46299│      0│28457.67│   45.56│    0.15│    3.51│2,546,445│1,273,204│200:46299
3s│    100│  70470│      0│28828.85│   45.56│    0.15│    3.47│3,875,850│1,291,943│200:70470
4s│    100│  94893│      0│29192.73│   45.56│    0.14│    3.43│5,219,115│1,304,771│200:94893
5s│    100│ 119602│      0│29516.55│   45.56│    0.14│    3.39│6,578,110│1,315,573│200:119602
6s│    100│ 144138│      0│29669.31│   45.56│    0.14│    3.37│7,927,590│1,321,143│200:144138
7s│    100│ 169391│      0│29952.31│   45.56│    0.14│    3.34│9,316,505│1,330,926│200:169391
8s│    100│ 194896│      0│30215.56│   45.56│    0.14│    3.31│10,719,280│1,339,904│200:194896
9s│    100│ 220623│      0│30489.03│   45.56│    0.14│    3.28│12,134,265│1,348,246│200:220623
10s│    100│ 246594│      0│30690.36│   45.56│    0.14│    3.26│13,562,670│1,356,265│200:246594
11s│    100│ 272641│      0│30889.66│   45.56│    0.14│    3.24│14,995,255│1,363,135│200:272641
12s│    100│ 299071│      0│31084.94│   45.56│    0.14│    3.22│16,448,905│1,370,737│200:299071
13s│    100│ 325628│      0│31266.74│   45.56│    0.14│    3.20│17,909,540│1,377,654│200:325628
14s│    100│ 352274│      0│31436.50│   45.56│    0.14│    3.18│19,375,070│1,383,930│200:352311
15s│    100│ 379019│      0│31602.18│   45.56│    0.14│    3.16│20,846,045│1,389,734│200:379019
16s│    100│ 405848│      0│31756.00│   45.56│    0.14│    3.15│22,321,640│1,395,101│200:405848
17s│    100│ 432456│      0│31883.06│   45.56│    0.14│    3.14│23,785,080│1,399,121│200:432456
18s│    100│ 459049│      0│32011.63│   45.56│    0.14│    3.12│25,247,695│1,402,645│200:459227
19s│    100│ 486589│      0│32163.75│   45.56│    0.14│    3.11│26,762,395│1,408,546│200:486589
20s│    100│ 513977│      0│32314.27│   45.56│    0.14│    3.09│28,268,735│1,413,435│200:513978
21s│    100│ 540328│      0│32372.83│   45.56│    0.14│    3.09│29,718,040│1,415,142│200:540328
22s│    100│ 568158│      0│32517.04│   45.56│    0.14│    3.08│31,248,690│1,420,373│200:568163
23s│    100│ 595715│      0│32614.98│   45.56│    0.14│    3.07│32,764,325│1,424,534│200:595715
24s│    100│ 623436│      0│32732.62│   45.56│    0.14│    3.06│34,288,980│1,428,599│200:623731
25s│    100│ 650742│      0│32858.38│   45.56│    0.14│    3.04│35,790,810│1,431,629│200:650745
26s│    100│ 678444│      0│32945.23│   45.56│    0.14│    3.04│37,314,420│1,435,163│200:678444
27s│    100│ 706591│      0│33066.18│   45.56│    0.14│    3.02│38,862,505│1,439,116│200:706591
28s│    100│ 734065│      0│33133.34│   45.56│    0.14│    3.02│40,373,575│1,441,912│200:734065
29s│    100│ 761927│      0│33222.32│   45.56│    0.14│    3.01│41,905,985│1,445,032│200:761934

40k的结果

可以达到40k,耗时很短,server 很高

PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
18814 root      20   0 2236976 187344   9532 R 548.5   0.6  25:16.16 main
17485 root      20   0 1748772  16324   9540 S 172.1   0.0  20:36.26 server
─────┬───────┬───────┬───────┬────────┬────────┬────────┬────────┬────────┬────────┬────────
耗时│ 并发数│ 成功数│ 失败数│   qps  │最长耗时│最短耗时│平均耗时│下载字节│字节每秒│ 状态码 
─────┼───────┼───────┼───────┼────────┼────────┼────────┼────────┼────────┼────────┼────────
247s│    100│7563722│      0│40026.69│   48.31│    0.14│    2.50│416,004,710│1,684,229│200:7563722
248s│    100│7595709│      0│40034.48│   48.31│    0.14│    2.50│417,763,995│1,684,532│200:7595709
249s│    100│7628010│      0│40044.40│   48.31│    0.14│    2.50│419,540,550│1,684,901│200:7628010
250s│    100│7659374│      0│40049.95│   48.31│    0.14│    2.50│421,265,570│1,685,058│200:7659374
251s│    100│7691688│      0│40060.62│   48.31│    0.14│    2.50│423,042,840│1,685,429│200:7691688
252s│    100│7723556│      0│40067.88│   48.31│    0.14│    2.50│424,795,580│1,685,695│200:7723556

结语

可以看到,go 来解决一个C10k的问题,很简单,当然这个只是最简单的一个模拟,下一期,我们加上数据库,再来解决数据库下面的C10k的问题。

参考

C10K