Go Concurrent Part.3 Context

讀這篇之前最好先看完 Pipelines and cancellation 補完一些前提知識。


Go Concurrency Patterns: Context

Go Concurrency Patterns: Context

Introduction

在 Go servers,每一個 request 交由他的 goroutine 處理。

Request handlers 通常會開啟額外的 goroutines 去存取後端,如資料庫 和 RPC 服務。

運作在 request 中的 goroutines,通常會需要存取特定的值(request-specific values),如終端使用者 ID,authorization tokens,request 的期限(deadline)

當一個 request 被取消 或 timeout,所有運作在這個 request 的 goroutines 應當快點結束,因此系統能多回收點資源來用。


在 Google,我們開發 context package,他可以很簡單的傳送 request 相關的值(request-scoped values),取消訊號,and deadlines across API boundaries to all the goroutines involved in handling a request.

Context

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 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,這個 channel 扮演著給 funcs 的 取消訊號

  • 當 channel 關閉後,funcs 應當拋棄他們的工作並 return

Err 方法 會回傳 error,他會解釋說為什麼這個 Context 被取消。

The Pipelines and Cancelation 這篇提到 Done channel 更多的說明。


Context 並沒有 取消方法,原因如同 Done channel 是 receive-only:

func 接收取消訊號通常不是 the one that sends the signal

特別是,當一個母操作開啟了子操作 goroutines,子操作不應該有能力把母操作關閉。

此外,WithCancel function 提供一個取消 new Context value 的方式。


Context 分給多個 goroutines 使用是安全低。

他可以傳出單一的 Context 給任意數量的 goroutines 然後 取消那個 Context 來發出訊號給所有 goroutines。


Deadline method 允許 funcs 決定他們是否該開始工作;

如果只有剩餘一點點時間,it may not be worthwhile。

Code 可能也使用 deadline 來設定 I/O 操作的 timeout。


Value 允許 Context 攜帶 request-scoped data

這個資料必須能安全的同時給多個 goroutines 使用。

Derived contexts

這個 Lib 提供了 funcs 從既有的 Context 推導出新的 Context Value。

這個值是個樹 form a tree:

當一個 Context 被取消後,其下的分支也會被關閉。


Background 是所有 Context 的 root; 且他絕不會被關閉:

1
2
3
4
// Background returns an empty Context. It is never canceled, has no deadline,
// and has no values. Background is typically used in main, init, and tests,
// and as the top-level Context for incoming requests.
func Background() Context

WithCancel and WithTimeout 回傳推導 Context values,他能夠比 母 Context 更快被關閉。

當 request handler returns 後,request 相關聯的 Context 會被取消。

當使用多個複製品(replicas),用 WithCancel 來取消冗餘(redundant)的 requests 也相當有用。

WithTimeout 設定 request 給後端 server 的 deadline 是有用的:

1
2
3
4
5
6
7
8
9
10
11
12
13
// WithCancel returns a copy of parent whose Done channel is closed as soon as
// parent.Done is closed or cancel is called.
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

// A CancelFunc cancels a Context.
type CancelFunc func()

// WithTimeout returns a copy of parent whose Done channel is closed as soon as
// parent.Done is closed, cancel is called, or timeout elapses. The new
// Context's Deadline is the sooner of now+timeout and the parent's deadline, if
// any. If the timer is still running, the cancel function releases its
// resources.
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)