【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)進行監控,一旦該信道被關閉,應主動終止對當前請求信息的處理,釋放資源並返回。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章