开发常用¶
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 的封装