Skip to content
Permalink
Browse files

新增加超时简写方式 update #105 (#115)

  • Loading branch information
guonaihong committed Dec 6, 2019
1 parent f401982 commit 2edae371e4896d87a980d69324da02a0c4195b27
Showing with 147 additions and 16 deletions.
  1. +62 −12 README.md
  2. +10 −0 group.go
  3. +60 −3 group_test.go
  4. +15 −1 req.go
@@ -36,6 +36,7 @@ gout 是go写的http 客户端,为提高工作效率而开发
- [number](#number)
- [duration](#duration)
- [rate](#rate)
- [timeout](#timeout)
- [proxy](#proxy)
- [cookie](#cookie)
- [context](#context)
@@ -417,27 +418,46 @@ func main() {
```
### SetHeader和BindHeader支持的更多类型
```go
package main
import (
"fmt"
"github.com/guonaihong/gout"
)
type testHeader struct {
CheckIn string `header:checkin`
CheckIn string `header:checkin`
CheckOut string `header:checkout`
}
t := testheader{}
func main() {
t := testHeader{}
code := 0
code := 0
if err := gout.GET(":8080/testquery").Code(&code).SetHeader(/*看下面支持的类型*/).BindHeader(&t).Do(); err != nil {
err := gout.
GET(":8080/testquery").
Code(&code).
SetHeader( /*看下面支持的类型*/ ).
BindHeader(&t).
Do()
if err != nil {
fmt.Printf("fail:%s\n", err)
}
}
```
* BindHeader支持的类型有
结构体
```go
// struct
type testHeader struct {
CheckIn string `header:checkin`
CheckOut string `header:checkout`
}
```
结构体
* SetHeader支持的类型有
```go
/*
@@ -567,18 +587,23 @@ func main() {
//打开debug模式
Debug(true).
//设置json到请求body
SetJSON(gout.H{"str": "foo",
"num": 100,
"bool": false,
"null": nil,
"array": gout.A{"foo", "bar", "baz"},
"obj": gout.H{"a": 1, "b": 2},
}).Do()
SetJSON(
gout.H{
"str": "foo",
"num": 100,
"bool": false,
"null": nil,
"array": gout.A{"foo", "bar", "baz"},
"obj": gout.H{"a": 1, "b": 2},
},
).
Do()
if err != nil {
fmt.Printf("err = %v\n", err)
}
}
/*
> POST /colorjson HTTP/1.1
> Content-Type: application/json
@@ -866,6 +891,7 @@ func main() {
```
## benchmark
### number
下面的例子,起了20并发。对:8080端口的服务,发送3000次请求进行压测,内容为json结构
```go
package main
@@ -896,6 +922,7 @@ func main() {
```
### duration
下面的例子,起了20并发。对:8080端口的服务,压测持续时间为10s,内容为json结构
```go
package main
@@ -927,6 +954,7 @@ func main() {
```
### rate
下面的例子,起了20并发。对:8080端口的服务,压测总次数为3000次,其中每秒发送1000次。内容为json结构
```go
package main
@@ -956,6 +984,28 @@ func main() {
}
}
```
## timeout
setimeout是request级别的超时方案。相比http.Client级别,更灵活。
```go
package main
import (
"fmt"
"github.com/guonaihong/gout"
"time"
)
func main() {
err := gout.GET(":8080").
SetTimeout(2 * time.Second).
Do()
if err != nil {
fmt.Printf("err = %v\n", err)
}
}
```
## proxy
* SetProxy 设置代理服务地址
@@ -8,6 +8,7 @@ import (
"net"
"net/http"
"net/url"
"time"
)

const (
@@ -197,7 +198,16 @@ func (g *routerGroup) Callback(cb func(*Context) error) *routerGroup {
return g
}

func (g *routerGroup) SetTimeout(d time.Duration) *routerGroup {
g.Req.index++
g.Req.timeoutIndex = g.Req.index
g.Req.timeout = d
return g
}

func (g *routerGroup) WithContext(c context.Context) *routerGroup {
g.Req.index++
g.Req.ctxIndex = g.Req.index
g.Req.c = c
return g
}
@@ -983,7 +983,7 @@ func TestCookie(t *testing.T) {

// Server side testing context function
func setupContext(t *testing.T) *gin.Engine {
router := gin.Default()
router := gin.New()

router.GET("/cancel", func(c *gin.Context) {
ctx := c.Request.Context()
@@ -999,9 +999,9 @@ func setupContext(t *testing.T) *gin.Engine {
ctx := c.Request.Context()
select {
case <-ctx.Done():
fmt.Printf("timeout done\n")
fmt.Printf("ctx timeout done\n")
case <-time.After(2 * time.Second):
assert.Fail(t, "test timeout fail")
assert.Fail(t, "test ctx timeout fail")
}
})

@@ -1219,3 +1219,60 @@ func TestWWWForm(t *testing.T) {
err := POST(ts.URL).Debug(true).SetWWWForm(need).Do()
assert.NoError(t, err)
}

func setup_group_timeout(t *testing.T) *gin.Engine {
router := gin.New()

router.GET("/timeout", func(c *gin.Context) {
ctx := c.Request.Context()
select {
case <-ctx.Done():
fmt.Printf("setTimeout done\n")
case <-time.After(2 * time.Second):
assert.Fail(t, "test timeout fail")
}
})

return router
}

func Test_Group_Timeout(t *testing.T) {
router := setup_group_timeout(t)
ts := httptest.NewServer(http.HandlerFunc(router.ServeHTTP))

const (
longTimeout = 400
middleTimeout = 300
shortTimeout = 200
)
// 只设置timeout
err := GET(ts.URL + "/timeout").
SetTimeout(shortTimeout * time.Millisecond).
Do()
assert.Error(t, err)

// 使用互斥api的原则,后面的覆盖前面的
// 这里是SetTimeout生效, 超时时间200ms
ctx, _ := context.WithTimeout(context.Background(), longTimeout*time.Millisecond)
s := time.Now()
err = GET(ts.URL + "/timeout").
WithContext(ctx).
SetTimeout(shortTimeout * time.Millisecond).
Do()

assert.Error(t, err)

assert.LessOrEqual(t, int(time.Now().Sub(s)), int(middleTimeout*time.Millisecond))

// 使用互斥api的原则,后面的覆盖前面的
// 这里是WithContext生效, 超时时间400ms
ctx, _ = context.WithTimeout(context.Background(), longTimeout*time.Millisecond)
s = time.Now()
err = GET(ts.URL + "/timeout").
SetTimeout(shortTimeout * time.Millisecond).
WithContext(ctx).
Do()

assert.Error(t, err)
assert.GreaterOrEqual(t, int(time.Now().Sub(s)), int(middleTimeout*time.Millisecond))
}
16 req.go
@@ -9,6 +9,7 @@ import (
"net/http"
"reflect"
"strings"
"time"
)

type Req struct {
@@ -36,14 +37,22 @@ type Req struct {
//cookie
cookies []*http.Cookie

timeout time.Duration

//自增id,主要给互斥API定优先级
//对于互斥api,后面的会覆盖前面的
index int
timeoutIndex int
ctxIndex int

c context.Context
err error
}

// req 结构布局说明,以decode为例
// body 可以支持text, json, yaml, xml,所以定义成接口形式
// headerDecode只有一个可能,就定义为具体类型。这里他们的decode实现也不一样
// 有没有必要,归一化成一种???
// 有没有必要,归一化成一种??? TODO:

func (r *Req) clone() Req {
return Req{
@@ -63,6 +72,7 @@ func (r *Req) clone() Req {
}

func (r *Req) Reset() {
r.index = 0
r.err = nil
r.cookies = nil
r.formEncode = nil
@@ -169,6 +179,10 @@ func (r *Req) request() (*http.Request, error) {
return nil, err
}

if r.timeout > 0 && r.timeoutIndex > r.ctxIndex {
r.c, _ = context.WithTimeout(context.Background(), r.timeout)
}

if r.c != nil {
req = req.WithContext(r.c)
}

0 comments on commit 2edae37

Please sign in to comment.
You can’t perform that action at this time.