参考:
安装 fasthttp
go get -u github.com/valyala/fasthttp
限流 fasthttp -> didi开源的包,注意版本哦 (uber-go / ratelimit 也挺牛逼)
go get github.com/didip/tollbooth@v1.0.0
go get github.com/didip/tollbooth_fasthttp
测试代码,注意写入写出json,没啥技巧,就硬抄😂
package main
import (
"encoding/json"
"fmt"
"github.com/didip/tollbooth"
"github.com/didip/tollbooth_fasthttp"
"github.com/valyala/fasthttp"
"log"
"strings"
"time"
)
var TLogger *log.Logger
var (
strContentType = []byte("Content-Type")
strApplicationJSON = []byte("application/json")
)
func Index(ctx *fasthttp.RequestCtx) {
fmt.Fprint(ctx, "Welcome!\n")
}
func Hello(ctx *fasthttp.RequestCtx) {
fmt.Fprintf(ctx, "hello, niubi")
}
type Output struct {
Message string `json:"message"`
Result string `json:"result"`
}
type Input struct {
Source string `json:"source"`
Mode string `json:"mode"`
}
func doJSONWrite(ctx *fasthttp.RequestCtx, code int, obj interface{}) {
ctx.Response.Header.SetCanonical(strContentType, strApplicationJSON)
ctx.Response.SetStatusCode(code)
if err := json.NewEncoder(ctx).Encode(obj); err != nil {
TLogger.Println(err)
ctx.Error(err.Error(), fasthttp.StatusInternalServerError)
}
}
var builder strings.Builder
func Hello_fix(ctx *fasthttp.RequestCtx) {
var output_one Output
var input_one Input
var paramErrMsg string
var statusCode int
body := ctx.PostBody()
if err := json.Unmarshal(body, &input_one); err != nil {
paramErrMsg = "请求参数错误,请检查输入格式"
statusCode = fasthttp.StatusBadRequest
// 这里的过程有点问题,无法解析
} else {
if len(strings.TrimSpace(input_one.Source)) > 0 {
// 连续的字符串拼接,虽然很快,但不适用这里
//builder.Write([]byte(input_one.Source))
//builder.Write([]byte("-->"))
//builder.WriteString("add-->")
//builder.Write([]byte(input_one.Mode))
//output_one.Result = builder.String()
output_one.Result = input_one.Source + "->" + input_one.Mode // 字符串拼接,简单粗暴,慢一点,跟python很像
output_one.Message = "ok"
doJSONWrite(ctx, fasthttp.StatusOK, output_one)
return
} else {
paramErrMsg = "长度为0"
statusCode = fasthttp.StatusBadRequest
}
output_one.Result = string(body)
output_one.Message = paramErrMsg
doJSONWrite(ctx, statusCode, output_one)
}
}
func main() {
//router := fasthttprouter.New() // 这里没有使用 fasthttprouter, 直接上,注意func中需要传入参数ctx
requestHandler := func(ctx *fasthttp.RequestCtx) {
switch string(ctx.Path()) {
case "/hello":
Hello(ctx)
case "/":
Index(ctx)
case "/hello_post":
//router.POST("/hello_post", Hello_fix)
Hello_fix(ctx)
default:
ctx.Error("Unsupporterd path", fasthttp.StatusNotFound)
}
}
limiter := tollbooth.NewLimiter(1, time.Second)
fasthttp.ListenAndServe(":8080", tollbooth_fasthttp.LimitHandler(requestHandler, limiter))
}
// 不启动 rate limit 的情况下
//func main() {
// router := fasthttprouter.New()
// router.GET("/hello", Hello)
// router.POST("/hello_post", Hello_fix)
// if err := fasthttp.ListenAndServe("0.0.0.0:8080", router.Handler); err != nil {
// TLogger.Panic("HTTP 服务启动失败")
// }
//}
压测效果:
global flags:
-cpus int
使用 CPU 的数量 (默认为 4 个)
-profile string
指定在执行期间启用哪个分析器,支持 cpu 和 heap。
-version
打印版本并退出。
attack command:
-body string
指定请求主体文件里的内容。
-cert string
指定用于 HTTPS 请求的 PEM 格式的客户端证书文件。如果 -key 未指定,它会被设置为这个标志的值。
-connections int
指定每个目标主机打开的空闲连接的最大数目,默认值为 10000。
-duration duration
指定发送请求到目标主机的时长,用 0 表示永久。
-header value
指定目标的请求头,可以重复指定多个请求头。
-http2
指定是否向支持的服务器发送 HTTP/2 请求,默认为:true。
-insecure
指定是否忽略无效的服务器 TLS 证书。
-keepalive
指定是否使用持久链接,默认值为:true。
-key string
指定 HTTPS 请求中使用的 PEM 编码的 SSL 客户端证书私钥文件。
-laddr value
指定要使用的本地 I P地址,默认值为:0.0.0.0。
-lazy
指定是否使用延迟模式读取目标。
-output string
指定输出文件的位置,默认为标准输出。
-rate uint
指定每秒钟对目标发送的请求数,默认值为:50。
-redirects int
指定每个请求的重定向的最大次数,默认为 10 次。当值为 -1, 不会遵循重定向但响应标记为成功。
-root-certs value
指定可信的 TLS 根证书文件,多个的情况下使用逗号分隔。如果未指定,使用系统默认的 CA 证书。
-targets string
指定目标文件,默认为标准输入。
-timeout duration
指定每个请求的超时时间,默认值为 30s。
-workers uint
指定初始化进程数量,默认值为 10。
report command:
-inputs string
指定报告输入文件,默认为标准输入。
-output string
指定报告输出文件,默认为标准输出。
-reporter string
指定要生成的报告的格式,支持 text,json, plot, hist[buckets]。默认为文本。
dump command:
-dumper string
指定转存文件,支持 json, csv 格式。默认为 json 格式。
-inputs string
指定要转存的输入文件,默认为标准输入,指定多个用逗号分隔。
-output string
指定要转存的输出文件,默认为标准输出。
搞一个 post hello_post 进行测试:
# 测试案例 POST - hello_post, 未进行流量限制
# 电脑默认开启的是 cpu4个一起搞
jq -ncM '{method: "POST", url: "http://localhost:8080/hello_post", body: {"source":"haha", "mode":"v2"} | @base64, header: {"Content-Type": ["application/json"]}}' |
vegeta -cpus 4 attack -format=json -rate=100000 -duration=1s | tee results.bin | vegeta report
Requests [total, rate, throughput] 50183, 50167.94, 24945.60
Duration [total, attack, wait] 1.005s, 1s, 5.048ms
Latencies [min, mean, 50, 90, 95, 99, max] 10.911µs, 6.136ms, 2.801ms, 16.89ms, 24.38ms, 38.505ms, 64.408ms
Bytes In [total, mean] 1053318, 20.99
Bytes Out [total, mean] 727291, 14.49
Success [ratio] 49.98%
Status Codes [code:count] 0:25104 200:25079
jq -ncM '{method: "POST", url: "http://localhost:8080/hello_post", body: {"source":"haha", "mode":"v2"} | @base64, header: {"Content-Type": ["application/json"]}}' |
vegeta -cpus 2 attack -format=json -rate=100000 -duration=1s | tee results.bin | vegeta report
Requests [total, rate, throughput] 40945, 37548.48, 8717.79
Duration [total, attack, wait] 1.152s, 1.09s, 61.785ms
Latencies [min, mean, 50, 90, 95, 99, max] 10.596µs, 45.69ms, 5.067ms, 137.514ms, 201.844ms, 249.92ms, 386.753ms
Bytes In [total, mean] 421890, 10.30
Bytes Out [total, mean] 291305, 7.11
Success [ratio] 24.53%
Status Codes [code:count] 0:30900 200:10045
jq -ncM '{method: "POST", url: "http://localhost:8080/hello_post", body: {"source":"haha", "mode":"v2"} | @base64, header: {"Content-Type": ["application/json"]}}' |
vegeta -cpus 4 attack -format=json -rate=50000 -duration=1s | tee results.bin | vegeta report
Requests [total, rate, throughput] 49999, 49957.40, 28333.98
Duration [total, attack, wait] 1.017s, 1.001s, 16.356ms
Latencies [min, mean, 50, 90, 95, 99, max] 10.719µs, 5.633ms, 2.491ms, 15.593ms, 22.69ms, 41.062ms, 90.625ms
Bytes In [total, mean] 1210482, 24.21
Bytes Out [total, mean] 835809, 16.72
Success [ratio] 57.64%
Status Codes [code:count] 0:21178 200:28821
jq -ncM '{method: "POST", url: "http://localhost:8080/hello_post", body: {"source":"haha", "mode":"v2"} | @base64, header: {"Content-Type": ["application/json"]}}' |
vegeta -cpus 4 attack -format=json -rate=30000 -duration=1s | tee results.bin | vegeta report
Requests [total, rate, throughput] 30000, 30003.12, 29837.60
Duration [total, attack, wait] 1s, 999.896ms, 251.244µs
Latencies [min, mean, 50, 90, 95, 99, max] 27.814µs, 847.483µs, 228.297µs, 1.702ms, 2.832ms, 12.973ms, 26.124ms
Bytes In [total, mean] 1253364, 41.78
Bytes Out [total, mean] 865418, 28.85
Success [ratio] 99.47%
Status Codes [code:count] 0:158 200:29842
jq -ncM '{method: "POST", url: "http://localhost:8080/hello_post", body: {"source":"haha", "mode":"v2"} | @base64, header: {"Content-Type": ["application/json"]}}' |
vegeta -cpus 4 attack -format=json -rate=20000 -duration=1s | tee results.bin | vegeta report
Requests [total, rate, throughput] 20000, 20002.51, 20000.21
Duration [total, attack, wait] 999.99ms, 999.874ms, 115.368µs
Latencies [min, mean, 50, 90, 95, 99, max] 55.093µs, 202.688µs, 115.003µs, 276.439µs, 569.742µs, 2.254ms, 5.523ms
Bytes In [total, mean] 840000, 42.00
Bytes Out [total, mean] 580000, 29.00
Success [ratio] 100.00%
Status Codes [code:count] 200:20000
# 测试如果进行 ratelimit 限制后的 压测效果
# 10w
jq -ncM '{method: "POST", url: "http://localhost:8080/hello_post", body: {"source":"haha", "mode":"v2"} | @base64, header: {"Content-Type": ["application/json"]}}' |
vegeta -cpus 4 attack -format=json -rate=100000 -duration=1s | tee results.bin | vegeta report
Requests [total, rate, throughput] 50743, 50463.75, 1.98
Duration [total, attack, wait] 1.011s, 1.006s, 5.626ms
Latencies [min, mean, 50, 90, 95, 99, max] 10.971µs, 4.655ms, 2.322ms, 12.943ms, 19.858ms, 31.756ms, 53.951ms
Bytes In [total, mean] 1142355, 22.51
Bytes Out [total, mean] 849439, 16.74
Success [ratio] 0.00%
Status Codes [code:count] 0:21452 200:2 429:29289
Error Set:
429 Too Many Requests
# 1w
jq -ncM '{method: "POST", url: "http://localhost:8080/hello_post", body: {"source":"haha", "mode":"v2"} | @base64, header: {"Content-Type": ["application/json"]}}' |
vegeta -cpus 4 attack -format=json -rate=10000 -duration=1s | tee results.bin | vegeta report
Requests [total, rate, throughput] 10000, 10006.90, 1.00
Duration [total, attack, wait] 999.409ms, 999.31ms, 98.71µs
Latencies [min, mean, 50, 90, 95, 99, max] 56.248µs, 166.948µs, 112.375µs, 201.068µs, 285.87µs, 1.203ms, 6.917ms
Bytes In [total, mean] 390003, 39.00
Bytes Out [total, mean] 290000, 29.00
Success [ratio] 0.01%
Status Codes [code:count] 200:1 429:9999
# 100
jq -ncM '{method: "POST", url: "http://localhost:8080/hello_post", body: {"source":"haha", "mode":"v2"} | @base64, header: {"Content-Type": ["application/json"]}}' |
vegeta -cpus 4 attack -format=json -rate=100 -duration=1s | tee results.bin | vegeta report
Requests [total, rate, throughput] 100, 100.85, 1.01
Duration [total, attack, wait] 992.305ms, 991.556ms, 748.715µs
Latencies [min, mean, 50, 90, 95, 99, max] 255.202µs, 748.089µs, 604.875µs, 1.097ms, 1.151ms, 3.835ms, 5.859ms
Bytes In [total, mean] 3903, 39.03
Bytes Out [total, mean] 2900, 29.00
Success [ratio] 1.00%
Status Codes [code:count] 200:1 429:99
Error Set:
429 Too Many Requests
# 10
# 确实效果感人,因为我们的 rate limt 中 用的是令牌桶算法 -> 限制了每秒需要通过请求最多是1个,不管来多少花里胡哨的并发,所以这里请求10个通过1个,请求100个也是通过1个
jq -ncM '{method: "POST", url: "http://localhost:8080/hello_post", body: {"source":"haha", "mode":"v2"} | @base64, header: {"Content-Type": ["application/json"]}}' |
vegeta -cpus 4 attack -format=json -rate=10 -duration=1s | tee results.bin | vegeta report
Requests [total, rate, throughput] 10, 11.11, 1.11
Duration [total, attack, wait] 901.188ms, 900.12ms, 1.068ms
Latencies [min, mean, 50, 90, 95, 99, max] 483.77µs, 1.529ms, 888.565µs, 4.442ms, 7.646ms, 7.646ms, 7.646ms
Bytes In [total, mean] 393, 39.30
Bytes Out [total, mean] 290, 29.00
Success [ratio] 10.00%
Status Codes [code:count] 200:1 429:9
报告字段解释:
所以重点关注下,并发情况下的吞吐量 - throughput (单位时间内的)
还能输出报告:
vegeta plot --title HelloGitHub results.bin > plot.html