点击↑上方↑蓝色“编了个程”关注我~
这是Yasin的第 57 篇原创文章
Y说
周末的快乐时光总是很短暂。
今天天气不错,有点太阳。去附近的商场吃了一顿“高老九重庆火锅”,味道还行,主要是好久没吃火锅了~
白天把家里好好收拾了一下,感觉心情也跟着变好了。
已经用Golang在日常工作中开发了好几个月了。作为一个Golang菜鸟,有些东西往往只是会用,没有来得及去深究其背后的原理和设计用意。今年默默给自己立了一个Flag,就是好好深入学习一下这门语言。
在用Golang的时候,发现很多下游的框架或服务通常会要求我们传入一个context.Context
对象,且它们一般在函数的第一个参数里。我们公司的框架里,每个请求都会有一个独一无二的「Log Id」,用来串联多个微服务的请求。有时候我们自己的代码可能用不上这个对象,但为了保持调用链的完整,日志不丢失,还是不得不传下去。
从协程说起
Golang这个语言的优势之一,就是它拥有一个高并发利器:goroutine。它是一个Golang语言实现的协程,单机就可以同时支持大量的并发请求,非常适合如今互联网时代的后端服务。
那有了大量的协程,就带来了一些问题。比如:请求的一些比较通用的参数(比如上面提到的Log Id)如何传递到协程呢?如何终止一个协程呢?
在Golang中,我们无法从外部终止一个协程,只能它自己结束。常见的比如超时取消等需求,我们通常使用抢占操作或者中断后续操作。
在context出来以前,Golang是channel + select的方式来做这件事情的。具体的做法是:定义一个channel,子协程启一个定时任务循环监听这个channel,主协程如果想取消子协程,就往channel里写入信号。
这样确实能解决这个问题,但编码麻烦不说,如果有协程里面启协程,形成协程树的话,就比较麻烦了,得定义大量的channel。
Context的接口
Context
是一个接口,位于context
包。它的接口定义非常简单:
// A Context carries a deadline, cancelation signal, and request-scoped values
// across API boundaries. Its methods are safe for simultaneous use by multiple
// goroutines.
type Context interface {
// Done returns a channel that is closed when this Context is canceled
// or times out.
Done() <-chan struct{}
// Err indicates why this context was canceled, after the Done channel
// is closed.
Err() error
// Deadline returns the time when this Context will be canceled, if any.
Deadline() (deadline time.Time, ok bool)
// Value returns the value associated with key or nil if none.
Value(key interface{}) interface{}
}
简单解释一下四个方法的作用:
Done:返回一个Channel,用于向当前协程传递是否结束;
Err:当Done Channel结束时,返回这个context为什么取消。如果是被取消&#x