context
context 可以在同一进程传递数据,但是HTTP请求需要手动做一些操作才能将值传递,比如客户端在请求服务端时在ctx里面传了一个值,但是在服务端收到ctx的时候拿着这个key是解析不到数据的
文章讲的蛮好的
1.context 可以携带key val 每次添加一对,context都会包一层,查找的时候会一层一层向上查找,直到找到
2.服务中串联日志就是将traceid作为一个key在代码中传递,最后日志中串联在一起
cloud.tencent.com/developer/a…
用来做什么
控制超时
传递kv
取消消息
总体来看,cancel() 方法的功能就是主动关闭 channel:c.done;递归地取消它的所有子节点;从父节点删除自己。达到的效果是通过关闭 channel,将取消信号传递给了它的所有子节点。goroutine 接收到取消信号的方式就是 select 语句中的读 c.done 被选中
一个很好的理解父子节点关系,理解cancelFunc 和 Done方法的 说法:
CancelFunc 和 Done 方法就像是电话的话筒和听筒, CancelFunc,用来告诉管辖范围内的所有 Context 要进行自我终结,而通过监听听筒 Done 方法,我们就能听到上游父级管理者的终结命令,总之,CancelFunc 是主动让下游结束,而 Done 是被上游通知结束
一个父节点生成一个带有 Done 方法的子节点,并且返回子节点的 CancelFunc 函数句柄,底层建立的父子关联,这样父节点可以通过自己CancelFunc控制子节点,子节点也可以通过done方法监听父节点是否关闭(这是被动跟随父节点取消的逻辑)
如果请求链路上的一个方法里面只是有context作为入参,但是内部并没有使用(比如select监听ctx.Done),那么context取消也不会影响这个方法,这个方法会正常一直执行到结束,所以必须显示的监听context的状态才能受到影响
context 的实现主要有emptyCtx(作为context链路的根)cancelCtx(可以主动取消的) timerCtx(有定时功能的) timerCtx是在cancelCtx基础之上加了定时功能,也就是说他也有主动取消的功能
任何一个context不管他是什么类型只要他的父节点取消了,他也会被取消,比如一个timerctx继承自cancelctx,cancelctx主动取消了,timerctx及时没有到定时的时间也会跟着取消
func f(ctx context.Context) {
to := time.Now()
select {
case <-ctx.Done():
fmt.Println(ctx.Err(), time.Since(to))
}
}
var closedchan = make(chan struct{})
func main() {
pctx := context.Background()
cctx, cancel := context.WithCancel(pctx)
tctx, _ := context.WithTimeout(cctx, 10*time.Second)
go f(tctx)
time.Sleep(2 * time.Second)
cancel()
time.Sleep(20 * time.Second)
}
返回
context canceled 2.003239491s
创建一个cancelctx,底层会用propagateCancel方法来建立当前新建节点和父节点的关系,1.如果父节点是cancel类型的,会把这个节点放到父节点的children数组里面,这样父节点取消的时候直接在遍历数组把该节点取消;2.如果不是cancel类型,该节点就要在一个协程里面通过select监听父节点的done通道保证父节点取消的时候自己能够知道并且也调用自己的cancel方法,如果父节点一直不取消也可以监听自己的done通道,得到自己已经取消的消息就返回了,可以看出和父节点建立关系,不是把自己放在父节点的数组里面,就是自己取监听父节点的通道
如果是自己主动取消的话需要把自己从父节点的数组中删除,这样父节点取消的时候就不会再次取消这个节点了
所谓取消context就是关闭通道,timer类型的只不过是加了一个定时器,增加一个时间到了自己主动取消的功能,所以他可以跟着父节点被动取消,自己不到时间主动取消,到时间自己主动取消三种情况
done是一个只读通道,监听关闭通道关闭之后,会返回默认的零值,不再阻塞
这个视频讲的好