一.定义
context是一个用于跨函数、跨goroutine传递请求范围数据、取消信号以及超时处理的包。
context主要用来做两件事:
1.安全传递数据:是指在请求执行上下文中线程安全地传递数据,依赖于WithValue方法。
2.控制链路
二.context包----核心方法
context包的核心API有四个:
1.context.WithValue: 设置键值对,并且返回一个新的context实例
2.context.WithVancel:
3.context.WithDeadline
4.context.WithTimeout: 三这都返回一个可取消的context实例,和取消函数
注意:context实例是不可变的,每一次都是新创建的
三.context包----Context接口
context接口核心API有四个:
1.Deadline: 返回过期时间,如果ok为false,说明没有设置过期时间
2.Done: 返回一个channel,一般用于监听Context实例的信号,比如说过期,或者说正常关闭
3.Err: 返回一个错误用于表达Context发生了什么。Canceled => 正常关闭,DeadlineExceeded => 过期超时。
4.Value:取值
四.使用案例
import (
"context"
"testing"
"time"
)
type mykey struct{}
func TestContext(t *testing.T) {
// 一般是链路起点,或者调用的起点
ctx := context.Background()
// 在你不确定 context 该调用啥的时候 用 TODO()
//ctx1 := context.TODO()
ctx = context.WithValue(ctx, mykey{}, "my-value")
//第二种用法 ctx = context.WithValue(ctx, "my-key", "my-value")
// 取值, 方式一,直接取出来断言类型 -- 容易panic,因为容易没有设置key和value导致断言失败
val := ctx.Value(mykey{}).(string)
t.Log(val)
// 方式二:推荐
newVal := ctx.Value("不存在的key")
val, ok := newVal.(string)
if !ok {
t.Log("类型不对")
return
}
t.Log(val)
}
func TestContext_WithCancel(t *testing.T) {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
// cancel是一个函数,用于放弃一个正在使用的context,只有第一次调用的时候有用
// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
// A CancelFunc may be called by multiple goroutines simultaneously.
// After the first call, subsequent calls to a CancelFunc do nothing.
// 用完 ctx 再去调用
// defer cancel()
go func() {
time.Sleep(time.Second)
cancel()
}()
// Done(): 返回一个channel, 一般用于监听Context实例的信号,比如说过期或者正常关闭
<-ctx.Done()
// Err(): 返回一个错误用于表达Context发生了什么。 Canceled => 正常关闭,DeadlineExceeded => 过期超时
t.Log("hello, cancel: ", ctx.Err())
}
func TestContext_WithDeadline(t *testing.T) {
ctx := context.Background()
ctx, cancel := context.WithDeadline(ctx, time.Now().Add(time.Second*3))
// Deadline(): 返回过期时间,如果ok为false,说明没有设置过期时间
deadline, _ := ctx.Deadline()
t.Log("deadline: ", deadline)
defer cancel()
<-ctx.Done()
t.Log("hello, timeout: ", ctx.Err())
}
func TestContext_WithTimeout(t *testing.T) {
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, time.Second*3)
deadline, _ := ctx.Deadline()
t.Log("deadline: ", deadline)
defer cancel()
<-ctx.Done()
t.Log("hello, timeoutL ", ctx.Err())
}
// context的实例之间存在父子关系:
// 1.当父亲取消或者超时,所有派生的子context都被取消或者超时
// 2.当找key的时候, 子context先看自己有没有,没有则去祖先里面找
// 控制是从上到下,查找是从下至上的
func TestContext_Parent(t *testing.T) {
ctx := context.Background()
parent := context.WithValue(ctx, "my-key", "my value")
child := context.WithValue(parent, "my-key", "my new value")
// 查找keu的时候,子类会先看自己有没有,没有才会往祖先里面查找
t.Log("parent my-key: ", parent.Value("my-key"))
t.Log("child my-key: ", child.Value("my-key"))
// context_test.go:87: parent my-key: my value
// context_test.go:88: child my-key: my new value
child2, cancel := context.WithTimeout(parent, time.Second)
defer cancel()
t.Log("child2 my-key: ", child2.Value("my-key"))
// context_test.go:92: child2 my-key: my value
// 一般情况,父类无法获取子类的key,
child3 := context.WithValue(parent, "new-key", "child3 value")
t.Log("parent new-key: ", parent.Value("new-key"))
t.Log("child3 new-key: ", child3.Value("new-key"))
// context_test.go:95: parent new-key: <nil>
// context_test.go:96: child3 new-key: child3 value
// 逼不得已使用一个map
parent1 := context.WithValue(ctx, "map", map[string]string{})
child4, cancel := context.WithTimeout(parent1, time.Second)
defer cancel()
m := child4.Value("map")
m1, ok := m.(map[string]string)
if ok {
m1["key1"] = "value1"
}
nm := parent1.Value("map")
nm1, ok := nm.(map[string]string)
t.Log("parent1 key1: ", nm1["key1"])
// context_test.go:110: parent1 key1: value1
}
五.context包----valueCtx实现
valueCtx用于存储key-value数据,特点:
典型的装饰器模式:在已有Context接口的基础上附加一个存储 key-value的功能
只能存储一个key,value
六.context包----控制
context包提供了三个控制方法,WithCancel、WithDeadline和WithTimeout。三者用法大同小异:
1.没有过期时间,但是有需要在必要的时候取消,使用WithCancel
2.在固定时间点过期,使用WithDeadline
3.在一段时间后过期,使用WithTimeout
而后便是监听Done()返回的channel,不管是主动调用cancel()还是超时,都能从这个cannel里面取出来数据。后面可以用Err()方法来判断究竟是哪种情况。
父亲可以控制儿子,但是儿子控制不了父亲。(父亲拥有主动取消和设置取消时间的权限,儿子只能在父亲设置的取消时间范围内取消,父亲设置3秒超时取消,儿子不能设置超过3秒,但是可以设置小于3秒的过期取消时间)
七.context包----time.AfterFunc
八.context包----cancelCtx实现
九.context包----timerCtx实现