golang中context超时的一个例子

有时我们会利用 golang 去调用第三方接口,我们会像下面这样写:

package main

import (
	"io/ioutil"
	"log"
	"net/http"
)

func main() {
	req, err := http.NewRequest(http.MethodGet, "http://httpbin.org/get", nil)
	if err != nil {
		log.Fatal(err)
	}
	c := &http.Client{}
	res, err := c.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()
	out, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Fatal(err)
	}
	log.Println(string(out))
}

但是有时接口会因为某些原因,比如请求 MySQL 等,导致接口请求时间过长。所以我们希望接口在规定时间内就返回,比如 80 毫秒。如果接口 80 毫秒没有返回,我们会利用上下问的**context.WithTimeout**让接口超时返回。

我们需要修改代码像下面这样:

ctx, cancel := context.WithTimeout(context.Background(), time.Duration(time.Millisecond*80))
defer cancel()
req = req.WithContext(ctx)

我们首先定义一个指定超时的新上下文(使用 time.Duration)。 然后,我们使用 WithContext 将上下文添加到我们的请求中。 当创建带有超时的新上下文时,我们会收到一个 CancelFunc,它用于在上下文过期后清理资源。 这是有关 CancelFunc 的更多信息

调用 CancelFunc 会取消子项及其子项,删除父项对子项的引用,并停止任何关联的计时器。 调用 CancelFunc 失败会泄漏子项及其子项,直到父项被取消或计时器触发。 go vet 工具检查是否在所有控制流路径上使用了 CancelFuncs。

或者你也可以直接定义一个带有上下文的 request:

ctx, cancel := context.WithTimeout(context.Background(), time.Duration(time.Millisecond*80))
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://httpbin.org/get", nil)

下面是完整示例:

package main

import (
	"context"
	"io/ioutil"
	"log"
	"net/http"
	"time"
)

func main() {
	req, err := http.NewRequest(http.MethodGet, "http://httpbin.org/get", nil)
	if err != nil {
		log.Fatal(err)
	}
	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*80)
	defer cancel()
	req = req.WithContext(ctx)
	c := &http.Client{}
	res, err := c.Do(req)
	if err != nil {
		log.Fatal("request err:", err)
	}
	defer res.Body.Close()
	out, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Fatal(err)
	}
	log.Println(string(out))
}