Skip to content

Commit

Permalink
Customize bench (#162)
Browse files Browse the repository at this point in the history
* 开发自定义压测功能

* 新增测试代码

* 更新readme
  • Loading branch information
guonaihong committed Jan 14, 2020
1 parent 307d098 commit 0b5509b
Show file tree
Hide file tree
Showing 18 changed files with 296 additions and 120 deletions.
40 changes: 40 additions & 0 deletions README.md
Expand Up @@ -70,6 +70,7 @@ gout 是go写的http 客户端,为提高工作效率而开发
- [benchmarking a certain number of times](#benchmark-number)
- [benchmarking for a certain time](#benchmark-duration)
- [benchmarking at a fixed frequency](#benchmark-rate)
- [Custom benchmark functions](#Custom-benchmark-functions)
- [retry backoff](#retry-backoff)
- [import](#import)
- [send raw http request](#send-raw-http-request)
Expand Down Expand Up @@ -1269,6 +1270,45 @@ func main() {
}
```
### Custom benchmark functions
自定义压测函数,构造每次不一样的http request数据
```go
package main
import (
"fmt"
"github.com/google/uuid"
"github.com/guonaihong/gout"
"github.com/guonaihong/gout/filter"
"sync/atomic"
)
func main() {
i := int32(0)
err := filter.NewBench().
Concurrent(30). //开30个go程
Number(30000). //压测30000次
Loop(func(c *gout.Context) error {
// 下面的代码,每次生成不一样的http body 用于压测
uid := uuid.New() //生成uuid
id := atomic.AddInt32(&i, 1) //生成id, 可以理解为++i,线程安全版本
c.POST(":1234").SetJSON(gout.H{"sid": uid.String(),
"appkey": fmt.Sprintf("ak:%d", id),
"text": fmt.Sprintf("test text :%d", id)})
return nil
}).Do()
if err != nil {
fmt.Printf("err = %v\n", err)
}
}
```

## retry-backoff
retry 功能使用带抖动功能和指数的算法实现[backoff](http://www.awsarchitectureblog.com/2015/03/backoff.html)
```go
Expand Down
56 changes: 56 additions & 0 deletions _example/16e-customize-bench.go
@@ -0,0 +1,56 @@
package main

import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/guonaihong/gout"
"github.com/guonaihong/gout/filter"
"sync/atomic"
"time"
)

const (
benchNumber = 30000
benchConcurrent = 30
)

func server() {
router := gin.New()
router.POST("/", func(c *gin.Context) {
c.String(200, "hello world:gout")
})

router.Run()
}

func customize() {
i := int32(0)

err := filter.NewBench().
Concurrent(benchConcurrent).
Number(benchNumber).
Loop(func(c *gout.Context) error {

// 下面的代码,每次生成不一样的http body 用于压测
uid := uuid.New()
id := atomic.AddInt32(&i, 1)

c.POST(":8080").SetJSON(gout.H{"sid": uid.String(),
"appkey": fmt.Sprintf("ak:%d", id),
"text": fmt.Sprintf("test text :%d", id)})
return nil

}).Do()

if err != nil {
fmt.Printf("err = %v\n", err)
}
}

func main() {
go server()
time.Sleep(300 * time.Millisecond)

customize()
}
3 changes: 1 addition & 2 deletions _example/go.mod
Expand Up @@ -2,9 +2,8 @@ module main

require (
github.com/gin-gonic/gin v1.4.1-0.20190924141841-9b9f4fab34cc
github.com/google/uuid v1.1.1
github.com/guonaihong/gout v0.0.6
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
)

go 1.13
43 changes: 25 additions & 18 deletions bench/report.go
Expand Up @@ -50,14 +50,14 @@ type report struct {
type Report struct {
SendNum int // 已经发送的http 请求
report
Number int // 发送总次数
step int // 动态报表输出间隔
allResult chan result
waitQuit chan struct{} //等待startReport函数结束
allTimes []time.Duration
ctx context.Context
cancel func()
req *http.Request
Number int // 发送总次数
step int // 动态报表输出间隔
allResult chan result
waitQuit chan struct{} //等待startReport函数结束
allTimes []time.Duration
ctx context.Context
cancel func()
getRequest func() (*http.Request, error)

startTime time.Time
*http.Client
Expand All @@ -67,7 +67,14 @@ type Report struct {
}

// NewReport is a report initialization function
func NewReport(ctx context.Context, c, n int, duration time.Duration, req *http.Request, client *http.Client) *Report {
func NewReport(ctx context.Context,
c, n int,

duration time.Duration,

getRequest func() (*http.Request, error),

client *http.Client) *Report {
step := 0
if n > 150 {
if step = n / 10; step < 100 {
Expand All @@ -84,14 +91,14 @@ func NewReport(ctx context.Context, c, n int, duration time.Duration, req *http.
Duration: duration,
ErrMsg: make(map[string]int, 2),
},
waitQuit: make(chan struct{}),
Number: n,
step: step,
ctx: ctx,
cancel: cancel,
req: req,
Client: client,
startTime: time.Now(),
waitQuit: make(chan struct{}),
Number: n,
step: step,
ctx: ctx,
cancel: cancel,
getRequest: getRequest,
Client: client,
startTime: time.Now(),
}
}

Expand Down Expand Up @@ -129,7 +136,7 @@ func (r *Report) Process(work chan struct{}) {
for range work {
start := time.Now()

req, err := cloneRequest(r.req)
req, err := r.getRequest()
if err != nil {
r.addErrAndFailed(err)
continue
Expand Down
21 changes: 12 additions & 9 deletions bench/report_test.go
Expand Up @@ -57,10 +57,11 @@ func Test_Bench_Report_number(t *testing.T) {

ctx := context.Background()

req, err := newRequest(ts.URL)
assert.NoError(t, err)
getRequest := func() (*http.Request, error) {
return newRequest(ts.URL)
}

p := NewReport(ctx, 1, number, time.Duration(0), req, http.DefaultClient)
p := NewReport(ctx, 1, number, time.Duration(0), getRequest, http.DefaultClient)

runReport(p, number)

Expand All @@ -77,10 +78,11 @@ func Test_Bench_Report_duration(t *testing.T) {

ctx := context.Background()

req, err := newRequest(ts.URL)
assert.NoError(t, err)
getRequest := func() (*http.Request, error) {
return newRequest(ts.URL)
}

p := NewReport(ctx, 1, 0, 300*time.Millisecond, req, http.DefaultClient)
p := NewReport(ctx, 1, 0, 300*time.Millisecond, getRequest, http.DefaultClient)

runReport(p, number)

Expand All @@ -93,10 +95,11 @@ func Test_Bench_Report_fail(t *testing.T) {

ctx := context.Background()

req, err := newRequest("fail")
assert.NoError(t, err)
getRequest := func() (*http.Request, error) {
return newRequest("fail url")
}

p := NewReport(ctx, 1, number, time.Duration(0), req, http.DefaultClient)
p := NewReport(ctx, 1, number, time.Duration(0), getRequest, http.DefaultClient)

runReport(p, number)

Expand Down
21 changes: 0 additions & 21 deletions bench/req.go

This file was deleted.

33 changes: 0 additions & 33 deletions bench/req_test.go

This file was deleted.

17 changes: 17 additions & 0 deletions core/core.go
Expand Up @@ -2,6 +2,7 @@ package core

import (
"errors"
"net/http"
"reflect"
"unsafe"
)
Expand Down Expand Up @@ -63,3 +64,19 @@ func NewPtrVal(defValue interface{}) interface{} {
p.Elem().Set(reflect.ValueOf(defValue))
return p.Interface()
}

func CloneRequest(r *http.Request) (*http.Request, error) {
var err error

r0 := &http.Request{}
*r0 = *r

r0.Header = make(http.Header, len(r.Header))

for k, h := range r.Header {
r0.Header[k] = append([]string(nil), h...)
}

r0.Body, err = r.GetBody()
return r0, err
}
27 changes: 27 additions & 0 deletions core/core_test.go
@@ -1,7 +1,10 @@
package core

import (
"bytes"
"github.com/stretchr/testify/assert"
"io"
"net/http"
"reflect"
"testing"
)
Expand Down Expand Up @@ -71,3 +74,27 @@ func Test_Core_NewPtrVal(t *testing.T) {
assert.Equal(t, val.Interface(), v.set)
}
}

func Test_Bench_closeRequest(t *testing.T) {
b := bytes.NewBuffer([]byte("hello"))
req, err := http.NewRequest("GET", "hello", b)
assert.NoError(t, err)

req.Header.Add("h1", "h2")
req.Header.Add("h2", "h2")

req2, err := CloneRequest(req)
assert.NoError(t, err)

// 测试http header是否一样
assert.Equal(t, req.Header, req2.Header)

b2, err := req2.GetBody()
assert.NoError(t, err)

b3 := bytes.NewBuffer(nil)
io.Copy(b3, b2)

// 测试body是否一样
assert.Equal(t, b, b3)
}
1 change: 1 addition & 0 deletions dataflow/interface.go
Expand Up @@ -14,6 +14,7 @@ type Bencher interface {
Number(n int) Bencher
Rate(rate int) Bencher
Durations(d time.Duration) Bencher
Loop(func(c *Context) error) Bencher
Do() error
}

Expand Down

0 comments on commit 0b5509b

Please sign in to comment.