WithTimeout 的使用

下面的例子通过 WithTimeout 创建了一个带有超时的 context。在后面一个阻塞函数任务执行超时后,取消任务继续执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
"context"
"fmt"
"time"
)

const shortDuration = 1 * time.Millisecond

func main() {
// Pass a context with a timeout to tell a blocking function that it
// should abandon its work after the timeout elapses.
ctx, cancel := context.WithTimeout(context.Background(), shortDuration)
defer cancel()

select {
case <-time.After(1 * time.Second):
fmt.Println("overslept")
case <-ctx.Done():
fmt.Println(ctx.Err()) // prints "context deadline exceeded"
}

}

处理超时异常

通过 ctx.Err() 可以捕获到一个 Context Deadline Exceeded 异常。当 ctx.Done(),被执行后,就可以捕获到并通过 errors.Is() 来判断这个异常是否为 context.DeadlineExceeded

1
2
3
if errors.Is(err, context.DeadlineExceeded) {
log.Println("ContextDeadlineExceeded: true")
}

HTTP

Context Deadline Exceeded 常用在 HTTP 请求的上下文。设置截止日期或超时设置(即请求应中止的时间),并捕获在 Go 中发生的错误。如果服务器响应的时间大于设置的超时时间,则返回此错误。为请求设置超时是生产环境中的一个好习惯,可确保您始终在有限时间内获得响应(或错误)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package main

import (
"context"
"errors"
"log"
"net/http"
"os"
"time"
)

func slowServer(w http.ResponseWriter, r *http.Request) {
time.Sleep(10 * time.Second)
w.Write([]byte("Hello world!"))
}

func call() error {
client := &http.Client{}
req, err := http.NewRequest(http.MethodGet, "http://localhost:8080", nil)
if err != nil {
return err
}
ctx, cancel := context.WithTimeout(req.Context(), 1*time.Second)
defer cancel()
req = req.WithContext(ctx)
_, err = client.Do(req)
return err
}

func main() {
// run slow server
go func() {
http.HandleFunc("/", slowServer)

if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}()

time.Sleep(1 * time.Second) // wait for server to run

// call server
err := call()
if errors.Is(err, context.DeadlineExceeded) {
log.Println("ContextDeadlineExceeded: true")
}
if os.IsTimeout(err) {
log.Println("IsTimeoutError: true")
}
if err != nil {
log.Fatal(err)
}
}
1
2
3
2021/08/19 06:39:09 ContextDeadlineExceeded: true
2021/08/19 06:39:09 IsTimeoutError: true
2021/08/19 06:39:09 Get "http://localhost:8080": context deadline exceeded