开发常用¶
context 包¶
Go 语言中的每一个请求的都是通过一个单独的 Goroutine 进行处理的,HTTP/RPC 请求的处理器往往都会启动新的 Goroutine 访问数据库和 RPC 服务,我们可能会创建多个 Goroutine 来处理一次请求,而 Context
的主要作用就是在不同的 Goroutine 之间同步请求特定的数据、取消信号以及处理请求的截止日期。
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{}
}
Deadline
方法需要返回当前Context
被取消的时间,也就是完成工作的截止日期;Done
方法需要返回一个Channel
,这个Channel
会在当前工作完成或者上下文被取消之后关闭,多次调用Done
方法会返回同一个Channel
;Err
方法会返回当前Context
结束的原因,它只会在Done
返回的Channel
被关闭时才会返回非空的值;如果当前
Context
被取消就会返回Canceled
错误;如果当前
Context
超时就会返回DeadlineExceeded
错误;
Value
方法会从Context
中返回键对应的值,对于同一个上下文来说,多次调用Value
并传入相同的Key
会返回相同的结果,这个功能可以用来传递请求特定的数据;
Context 的实现方法¶
根context¶
Context 虽然是个接口,但是并不需要使用方实现:Background()
TODO
var (
background = new(emptyCtx)
todo = new(emptyCtx)
)
func Background() Context {
//主要用于main函数、初始化以及测试代码中,作为Context这个树结构的最顶层的Context,也就是根Context,它不能被取消
return background
}
func TODO() Context { //只在不确定时使用 context.TODO()
return todo
}
一般在代码中,开始上下文的时候都是以这两个作为最顶层的parent context,然后再衍生出子context。这些 Context
对象形成一棵树:当一个 Context
对象被取消时,继承自它的所有Context
都会被取消。
继承context¶
一般情况下是以context.Background()
做为根节点
//传递一个父Context作为参数,返回子Context,以及一个取消函数用来取消Context。
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
//会多传递一个截止时间参数,意味着到了这个时间点,会自动取消Context,也可以提前通过取消函数进行取消。
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
//超时自动取消,是多少时间后自动取消Context的意思。也可以提前通过取消函数进行取消。
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
// 为了生成一个绑定了一个键值对数据的Context,这个绑定的数据可以通过Context.Value方法访问到,这是我们实际用经常要用到的技巧,一般我们想要通过上下文来传递数据时,可以通过这个方法,如我们需要tarce追踪系统调用栈的时候
func WithValue(parent Context, key, val interface{}) Context
定时器¶
time.Timer¶
Timer
的结构定义
type Timer struct {
C <-chan Time
r runtimeTimer
}
其他方法¶
func After(d Duration) <-chan Time { return NewTimer(d).C }
根据源码可以看到After
直接是返回了Timer
的channel
,这种就可以做超时处理。
比如我们有这样一个需求:我们写了一个爬虫,爬虫在HTTP GET 一个网页的时候可能因为网络的原因就一只等待着,这时候就需要做超时处理,比如只请求五秒,五秒以后直接丢掉不请求这个网页了,或者重新发起请求。
Get("http://baidu.com/")
func Get(url string) {
response := make(chan string)
response = http.Request(url)
select {
case html :=<- response:
println(html)
case <-time.After(time.Second * 5):
println("超时处理")
}
}
可以从代码中体现出来,如果五秒到了,网页的请求还没有下来就是执行超时处理,因为Timer
的内部会是帮你在你设置的时间长度后自动向Timer.C
中写入当前时间。
其实也可以写成这样:
func Get(url string) {
response := make(chan string)
response = http.Request(url)
timeOut := time.NewTimer(time.Second * 3)
select {
case html :=<- response:
println(html)
case <-timeOut.C:
println("超时处理")
}
}
func (t *Timer) Reset(d Duration) bool
//强制的修改timer
中规定的时间,Reset
会先调用stopTimer
再调用startTimer
,类似于废弃之前的定时器,重新启动一个定时器,Reset
在Timer
还未触发时返回true
;触发了或Stop
了,返回false
。func (t *Timer) Stop() bool
// 如果定时器还未触发,Stop
会将其移除,并返回true
;否则返回false
;后续再对该Timer
调用Stop
,直接返回false
。func AfterFunc(d Duration, f func()) *Timer
// 在时间d后自动执行函数ffunc main() { f := func(){fmt.Println("I Love You!")} time.AfterFunc(time.Second*2, f) time.Sleep(time.Second * 4) }
time.Ticker¶
其实Ticker
就是一个重复版本的Timer
,它会重复的在时间d后向Ticker
中写数据
func NewTicker(d Duration) *Ticker
// 新建一个Tickerfunc (t *Ticker) Stop()
// 停止Tickerfunc Tick(d Duration) <-chan Time
// Ticker.C 的封装