context 用来解决 goroutine 之间退出通知
、元数据传递
的功能。
context 使用起来非常方便。源码里对外提供了一个创建根节点 context 的函数:
func Background() Context
background 是一个空的 context, 它不能被取消,没有值,也没有超时时间。
有了根节点 context,又提供了四个函数创建子节点 context:
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) func WithValue(parent Context, key, val interface{}) Context
注意:
- 不要将 Context 塞到结构体里。直接将 Context 类型作为函数的第一参数,而且一般都命名为 ctx。
- 不要向函数传入一个 nil 的 context,如果你实在不知道传什么,标准库给你准备好了一个 context:todo。
- 不要把本应该作为函数参数的类型塞到 context 中,context 存储的应该是一些共同的数据。例如:登陆的 session、cookie 等。
- 同一个 context 可能会被传递到多个 goroutine,别担心,context 是并发安全的。
传递共享的数据 #
对于 Web 服务端开发,往往希望将一个请求处理的整个过程串起来,这就非常依赖于 Thread Local(对于 Go 可理解为单个协程所独有) 的变量,而在 Go 语言中并没有这个概念,因此需要在函数调用的时候传递 context。
package main import ( "context" "fmt" ) func main() { ctx := context.Background() process(ctx) ctx = context.WithValue(ctx, "traceId", "qcrao-2019") process(ctx) } func process(ctx context.Context) { traceId, ok := ctx.Value("traceId").(string) if ok { fmt.Printf("process over. trace_id=%s\n", traceId) } else { fmt.Printf("process over. no trace_id\n") } }
结果
process over. no trace_id
process over. trace_id=qcrao-2019
第一次调用 process 函数时,ctx 是一个空的 context,自然取不出来 traceId。第二次,通过 WithValue
函数创建了一个 context,并赋上了 traceId
这个 key,自然就能取出来传入的 value 值。
当然,现实场景中可能是从一个 HTTP 请求中获取到的 Request-ID。所以,下面这个样例可能更适合:
const requestIDKey int = 0 func WithRequestID(next http.Handler) http.Handler { return http.HandlerFunc( func(rw http.ResponseWriter, req *http.Request) { // 从 header 中提取 request-id reqID := req.Header.Get("X-Request-ID") // 创建 valueCtx。使用自定义的类型,不容易冲突 ctx := context.WithValue( req.Context(), requestIDKey, reqID) // 创建新的请求 req = req.WithContext(ctx) // 调用 HTTP 处理函数 next.ServeHTTP(rw, req) } ) } // 获取 request-id func GetRequestID(ctx context.Context) string { ctx.Value(requestIDKey).(string) } func Handle(rw http.ResponseWriter, req *http.Request) { // 拿到 reqId,后面可以记录日志等等 reqID := GetRequestID(req.Context()) ... } func main() { handler := WithRequestID(http.HandlerFunc(Handle)) http.ListenAndServe("/", handler) }
标签:context,http,Context,process,ctx,golang,func From: https://www.cnblogs.com/xingxia/p/golang_context.html