【go】context上下文

Why

很多框架中接口函数第一个参数统一是​ctx context.Context​接口,如net/http中conn.serve(ctx context.Context)方法,为什么要这么设计呢?

因为一般一个网络请求Request,会在多个Goroutine中处理,而这些Goroutine可能需要共享Request的一些信息;同时当Request被取消或者超时的时候,所有从这个Request创建的所有Goroutine也应该被结束。上下文则几乎已经成为传递与请求同生存周期变量的标准方法。

What

​context​用于Goroutine之间共享状态变量,另一个gorutine通过设置ctx变量值,传递过期或撤销信号给被调用的程序单元。

type Context interface {

    Deadline() (deadline time.Time, ok bool)

    Done() <-chan struct{}

    Err() error

    Value(key interface{}) interface{}

}
  • ​Deadline​会返回一个超时时间,Goroutine获得了超时时间后,例如可以对某些io操作设定超时时间。
  • ​Done​方法返回一个信道(channel),传递是否已关闭的信号。
  • ​Err​方法表明​Context​被撤的原因。
  • ​Value​可以让Goroutine共享一些数据,当然获得数据是协程安全的。

How

Goroutine的创建和调用关系总是像层层调用的,而更靠顶部的Goroutine应有办法主动关闭其下属的Goroutine。

Context结构也应该像一棵树,要创建Context树,第一步就是要得到根节点,​context.Background​函数的返回值就是根节点。

创建子context 函数

解释

func Background() Context

该Context一般由接收请求的第一个Goroutine创建,即根节点,一般返回emptyCtx

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

创建子context,返回取消方法,父Goroutine可以调用取消子goroutine

func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)

创建子context,到了dealine或者被父gorutine调用返回的取消方法,终止

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)

创建子context,调用时间过了timeout或者被父gorutine调用返回的取消方法,终止

WithDeadline(parent, time.Now().Add(timeout))

func WithValue(parent Context, key interface{}, val interface{}) Context

返回valueCtx,传递了kv对到子context

子节点需要类似如下代码来接收是否已结束,并退出该Goroutine:

select {

    case <-cxt.Done():

        // do some clean...

}

Example

ctx := context.WithValue(baseCtx, ServerContextKey, srv)    // 服务创建子context
	
for {
	rw, err := l.Accept()	// 接收请求
	connCtx := ctx 	
	c := srv.newConn(rw)
	c.setState(c.rwc, StateNew) // before Serve can return
	go c.serve(connCtx)    // 子routine传入子ctx
}

Summary

​context​通过构建树型关系的Context,达到上一层Goroutine给下层Goroutine

父子传递控制信号&共享变量

  • Context对象生存周期一般仅为一个请求的处理周期。即对一个请求创建一个Context变量(它为Context树结构的根);在请求处理结束后,撤销此ctx变量,释放资源。
  • 每次创建Goroutine,可将原Context传递给Goroutine,也可创建一个子Context
  • Context能存储不同类型、不同数目的值KV,Goroutines安全读写。
  • 当通过父Context对象创建子Context对象时,可同时获得子Context的一个撤销函数,这样父Context对象的创建环境就获得了对子Context将要被传递到的Goroutine的撤销权。

使用原则

  1. 不作为结构体字段,按需显式地函数间传参,作为第一个参数使用,一般命名为ctx;
  2. 不要传入一个nil的Context,如果你不确定你要用什么Context的时候传一个context.TODO
  3. 共享变量Values只用于请求scope的数据,不传可选的参数;
  4. 同个Context可传到不同的goroutine中,在多个goroutine中是线程安全的
  5. 在子Context被传递到的goroutine中,应该对该子Context的Done信道(channel)进行监控,一旦该信道被关闭,应主动终止对当前请求信息的处理,释放资源并返回。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章