压力测试之前,先看下单元测试,主要怕自己忘记,仅供参考
1.普通应用程序 单元+压力测试
(1). 普通单元测试
go test是golang的单元测试命令
package main
import (
"testing"
)
func Testfunc (t *testing.T) {
逻辑
}
go test -v test.go 代表运行test.go中所有的以Testf命名开始的函数
go test -v -run=“func” test.go 代表运行test.go中所有的Testffunc的函数
-run=“正则表达”
-v 代表查看详情
go test是先编译然后运行,所以支持go build相关的参数
(2). 请求http单元测试
package main
import (
"testing"
"fmt"
"io/ioutil"
"net/http"
"flag"
"net/url"
"io"
"bytes"
"encoding/json"
"github.com/parnurzeal/gorequest"//gorequest需要go get
)
var (
//定义命令行参数,可以go test -arg=
host *string
ip *string
reqUrl string
sign *string
)
func init() {
//解析命令行参数
host = flag.String("host", "127.0.0.1", "请求域名")
ip = flag.String("ip", "127.0.0.1", "请求ip")
fromIp = flag.String("from_ip", "10.78.48.10", "请求来源ip")
sign = flag.String("sign", "c8abc2d3efa0081478beb66e0542eb62", "请求sign")
flag.Parse()
}
func TestReq (t *testing.T) {
send(t, "mock/req.json")
}
//mockFile 代表测试样本数据
func send(t *testing.T, mockFile string) {
if mockFile == "" {
mockFile = "mock/req.json"
}
defer func() {
if err := recover(); err != nil {
fmt.Println("err =", err)
}
}()
myRequest := url.Values{
}
myRequest.Set("sign", *sign)
myRequest.Set("t", "1554198282793")
myRequest.Set("syn" , "169dd6fc154")
myRequest.Set("n" , "1")
reqUrl = "http://" + *ip + "/req/" + "?" + myRequest.Encode()
reqBody, err := ioutil.ReadFile(mockFile)
if err != nil {
error(err.Error()})
return
}
Request := gorequest.New()
Request.Header = map[string]string{
"X-Forwarded-For": *fromIp,
"Host": *host,
"Content-type" : "application/gzip",
}
resp, data, errs := Request.Post(reqUrl).Type("text").Send(string(reqBody)).EndBytes()
errStr := make([]string, 0)
for _, err := range errs {
if err != nil {
errStr = append(errStr, err.Error())
return
}
}
if resp.StatusCode != http.StatusOK {
error([]string{"return status code error", resp.StatusCode})
return
}
}
1:需要go get github.com/parnurzeal/gorequest
2:单元测试go test -v test.go -args -ip=test.com -host=test.com -sign=ccbb608d5c618ff7d55bedc19de4138f
-args后面跟参数列表
(3). 请求http压力测试
package main
import (
"testing"
"fmt"
"io/ioutil"
"net/http"
"flag"
"net/url"
"io"
"bytes"
"encoding/json"
"github.com/parnurzeal/gorequest"//gorequest需要go get
)
var (
//定义命令行参数,可以go test -arg=
host *string
ip *string
reqUrl string
sign *string
)
func init() {
//解析命令行参数
host = flag.String("host", "127.0.0.1", "请求域名")
ip = flag.String("ip", "127.0.0.1", "请求ip")
fromIp = flag.String("from_ip", "10.78.48.10", "请求来源ip")
sign = flag.String("sign", "c8abc2d3efa0081478beb66e0542eb62", "请求sign")
flag.Parse()
}
//压力测试程序
func BenchmarkSend (b *testing.B) {
t := new(testing.T)
b.ReportAllocs()
for i := 0; i < b.N; i++ {
send(t, "")
}
}
//mockFile 代表测试样本数据
func send(t *testing.T, mockFile string) {
if mockFile == "" {
mockFile = "mock/req.json"
}
defer func() {
if err := recover(); err != nil {
fmt.Println("err =", err)
}
}()
myRequest := url.Values{
}
myRequest.Set("sign", *sign)
myRequest.Set("t", "1554198282793")
myRequest.Set("syn" , "169dd6fc154")
myRequest.Set("n" , "1")
reqUrl = "http://" + *ip + "/req/" + "?" + myRequest.Encode()
reqBody, err := ioutil.ReadFile(mockFile)
if err != nil {
error(err.Error()})
return
}
Request := gorequest.New()
Request.Header = map[string]string{
"X-Forwarded-For": *fromIp,
"Host": *host,
"Content-type" : "application/gzip",
}
resp, data, errs := Request.Post(reqUrl).Type("text").Send(string(reqBody)).EndBytes()
errStr := make([]string, 0)
for _, err := range errs {
if err != nil {
errStr = append(errStr, err.Error())
return
}
}
if resp.StatusCode != http.StatusOK {
error([]string{"return status code error", resp.StatusCode})
return
}
}
压力测试:
go test -v -run=“none” -bench=“Send*” -benchtime=10s test.go
如果是带 cpu和内存分析的
go test -v -run=“none” -bench=“Send*” -benchtime=10s -cpuprofile cpu.out -memprofile mem.out test.go
简单的看cpu效果
输出 上面cpu和内存数据 到图片
go tool pprof -png cpu.out > cpu.png
也可以直接进入命令行,help查询相关命令:
go tool pprof cput.out
不过这种http请求压测看内存和trace数据没有什么意义
下面介绍不同过http协议,不运行网络http请求,直接压测函数内部调用http handler函数,我使用的框架是echo
2.web接口压力测试
通过压测函数直接调用内部handler函数
我使用的框架是echo,其他框架应该也差不多,下面代码仅供参考
package main
import (
"testing"
"fmt"
"io/ioutil"
"net/http"
"flag"
"net/url"
"io"
"bytes"
"encoding/json"
"github.com/parnurzeal/gorequest"//gorequest需要go get
)
var (
//定义命令行参数,可以go test -arg=
host *string
ip *string
reqUrl string
sign *string
)
func init() {
//解析命令行参数
host = flag.String("host", "127.0.0.1", "请求域名")
ip = flag.String("ip", "127.0.0.1", "请求ip")
fromIp = flag.String("from_ip", "10.78.48.10", "请求来源ip")
sign = flag.String("sign", "c8abc2d3efa0081478beb66e0542eb62", "请求sign")
flag.Parse()
}
//压力测试程序
func BenchmarkSend (b *testing.B) {
//构建请求对象
testData, _ := ioutil.ReadFile("mock/req.json")
req, _ := http.NewRequest("POST", "/req_url", io.Reader(bytes.NewReader(testData)))
req.Header.Set("X-Forwarded-For" , "10.78.48.10")
//new一个response对象
res := new(http.ResponseWriter)
//new 一个context的函数
cont := echo.New().NewContext(req, *res)
b.ReportAllocs()
for i := 0; i < b.N; i++ {
req.Body = ioutil.NopCloser(io.Reader(bytes.NewReader(testData)))
//handles包代表http handler的包
//Handlers.Send代表handles包内部的真正逻辑函数
handlers.Send.Post(cont)
}
}
因为是直接调用的内部handler函数,所以CPU消耗和内存都比较真实
如果是带 cpu和内存分析的
go test -v -run=“none” -bench=“Send*” -benchtime=10s -cpuprofile cpu.out -memprofile mem.out test.go
如果要分析携程调用和网络阻塞:
go test -v -run=“none” -bench=“Send*” -benchtime=10s -trace=trace.out test.go
go tool trace -http=“host:port” trace.out
访问http:host:port即可看到