context 上下文管理
主要功能:
- 全局变量设置
- 通知goroutine退出
通知goroutine退出
使用context.Done()通道是否有数据判断超时时间
- 如果到达超时时间,ctx就会往超时通道放一个数据
- ctx.Done()是从超时通道中获取一个消息,如果获取到了,说明超时时间已经到了。
- 然后执行该请求的关闭操作
package main
import (
"io/ioutil"
"time"
"net/http"
"context"
"fmt"
)
type Result struct{
r *http.Response
err error
}
func process(){
// 生成一个context超时多对象
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
// 最后关闭这个对象
defer cancel()
//生成http的相关对象,和返回结果的管道
tr := &http.Transport{}
client := &http.Client{Transport: tr}
resultChan := make(chan Result, 1)
// 生成一个http请求的对象
req, err := http.NewRequest("GET", "https://www.google.com.hk", nil)
if err != nil{
fmt.Println("http request failed, err:", err)
return
}
// 使用goroutine 执行开始请求并把结果返回到resultChan通道中
go func(){
resp, err := client.Do(req)
pack := Result{r: resp, err: err}
resultChan <- pack
}()
// 检查两个通道判断请求的结果
// ctx.Done()是一个超时的通道,如果之行后就会在指定的超时时间点放入一个消息到这个通道中
// resultChan 是请求结果的通道,如果这个通道有数据说明请求结果完成。
select {
case <- ctx.Done():
// 是否能在超时时间通道获取数据,如果获取到就直接把该请求取消。
tr.CancelRequest(req)
res := <- resultChan
fmt.Println("Time out, err:", res.err)
case res := <- resultChan :
// 如果有数据打印请求的数据
defer res.r.Body.Close()
out, _ := ioutil.ReadAll(res.r.Body)
fmt.Printf("server Respones :%s", out)
}
}
func main(){
process()
}
编译并运行:
$ go build go_dev/day12/exercise/context
$ ./context
Time out, err: Get https://www.google.com.hk: net/http: request canceled while waiting for connection
网址更改为http://www.baidu.com 重新编译运行
$ go build go_dev/day12/exercise/context
$ ./context
server Respones :<!DOCTYPE html>
<!--STATUS OK--> 等html源代码
数据的设置和继承
继承是在实例化新的对象时,引用已有的对象
package main
import (
"context"
"fmt"
)
func process(ctx context.Context) {
ret, ok := ctx.Value("trace_id").(int)
if !ok {
ret = 654321
}
fmt.Printf("ret:%d\n", ret)
user_id, ok := ctx.Value("user_id").(string)
if !ok {
fmt.Println("Can't get user_id")
}
fmt.Println("user_id:", user_id)
return
}
func main() {
// context.Background() 可以理解为一个基类
ctx := context.WithValue(context.Background(), "trace_id", 123456)
// 之后再设置可以引用上面的ctx(继承)
ctx = context.WithValue(ctx, "user_id", "9fd14d74-9fde-48ff-a060-b18d27cdf201")
process(ctx)
}
变异并运行:
$ go build go_dev/day12/exercise/ctx_value
$ ./ctx_value
ret:123456
user_id: 9fd14d74-9fde-48ff-a060-b18d27cdf201
主程序完成后自动关闭所有的goroutine
context.WithCanceln 自动通知goroutine退出的原理很简单,
- 就是在cancle()的时候,自动向chan发送一个信号数据,
- goroutine通过ctx.Done()获取数据后自动退出
package main
import (
"time"
"fmt"
"context"
)
func gen(ctx context.Context) <-chan int{
dst := make(chan int)
n := 1
go func(){
for{
select{
case <- ctx.Done():
fmt.Println(n, "exit")
return
case dst <- n:
n++
}
}
}()
return dst
}
func test(){
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
intChan := gen(ctx)
for n := range intChan{
fmt.Println(n)
if n == 5{
break
}
}
}
func main(){
test()
time.Sleep(time.Hour)
}