github.com/code-reading/golang@v0.0.0-20220303082512-ba5bc0e589a3/docs/004-context包源码分析.md (about) 1 2 ## Context 3 4 [分析示例:coding/context](../coding/context) 5 6 [源码位置:/src/context](../go/src/contex) 7 8 [代码设计:/design/context](../design/context.pu) 9 10 context 用来解决 goroutine 之间退出通知、元数据(token, trace_id等等)传递的功能 11 ### Context 接口 12 Context 其实是定义了一个接口, 那么用户可以通过实现这个接口而自定义Context 13 14 ```go 15 type Context interface { 16 Deadline() (deadline time.Time, ok bool) 17 Done() <-chan struct{} 18 Err() error 19 Value(key interface{}) interface{} 20 } 21 ``` 22 Deadlne方法:当Context自动取消或者到了取消时间被取消后返回 23 Done方法:当Context被取消或者到了deadline返回一个被关闭的channel 24 Err方法:当Context被取消或者关闭后,返回context取消的原因 25 Value方法:获取设置的key对应的值 26 27 这个接口主要被三个类继承实现,分别是emptyCtx、ValueCtx、cancelCtx,采用匿名接口的写法,这样可以对任意实现了该接口的类型进行重写。 28 29 基于emptyCtx, ValueCtx , cancelCtx 这几个类 有派生出如下接个功能函数 30 31 ```go 32 func WithCancel(parent Context) (ctx Context, cancel CancelFunc) 33 func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) 34 func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) 35 func WithValue(parent Context, key, val interface{}) Context 36 ``` 37 38 ### cancler 接口 39 40 ```go 41 type canceler interface { 42 cancel(removeFromParent bool, err error) 43 Done() <-chan struct{} 44 } 45 ``` 46 实现了上面定义的两个方法的 Context,就表明该 Context 是可取消的。源码中有两个类型实现了 canceler 接口:*cancelCtx 和 *timerCtx。注意是加了 * 号的,是这两个结构体的指针实现了 canceler 接口。 47 48 49 ### 创建context 50 context包主要提供了两种方式创建context: 51 ```go 52 context.Backgroud() 53 context.TODO() 54 ``` 55 这两个函数其实只是互为别名,没有差别,官方给的定义是: 56 57 - context.Background 是上下文的默认值,所有其他的上下文都应该从它衍生(Derived)出来。 58 - context.TODO 应该只在不确定应该使用哪种上下文时使用; 59 60 61 ### context场景 62 context的作用就是在不同的goroutine之间同步请求特定的数据、取消信号以及处理请求的截止日期。 63 64 context 可以在函数之间传递一些公共参数,比如全局日志追踪的签名,trace_id等, 但是不要传递业务参数, 一是因为context传递的参数都是interface{} 类型,需要来回转换; 二是隐性传参,导致代码可读性差,维护很难; 65 66 #### 传值 67 68 ```go 69 const ( 70 KEY = "trace_id" 71 ) 72 73 func NewRequestID() string { 74 return strings.Replace(uuid.New().String(), "-", "", -1) 75 } 76 77 func NewContextWithTraceID() context.Context { 78 ctx := context.WithValue(context.Background(), KEY,NewRequestID()) 79 return ctx 80 } 81 82 func PrintLog(ctx context.Context, message string) { 83 fmt.Printf("%s|info|trace_id=%s|%s",time.Now().Format("2006-01-02 15:04:05") , GetContextValue(ctx, KEY), message) 84 } 85 86 func GetContextValue(ctx context.Context,k string) string{ 87 v, ok := ctx.Value(k).(string) 88 if !ok{ 89 return "" 90 } 91 return v 92 } 93 94 func ProcessEnter(ctx context.Context) { 95 PrintLog(ctx, "Golang梦工厂") 96 } 97 98 99 func main() { 100 ProcessEnter(NewContextWithTraceID()) 101 } 102 ``` 103 104 output 105 ```shell 106 2021-10-31 15:13:25|info|trace_id=7572e295351e478e91b1ba0fc37886c0|Golang梦工厂 107 Process finished with the exit code 0 108 ``` 109 110 #### 超时控制 111 通常健壮的程序都是要设置超时时间的,避免因为服务端长时间响应消耗资源,withTimeout或者withDeadline来做超时控制,当一次请求到达我们设置的超时时间,就会及时取消,不在往下执行。withTimeout和withDeadline作用是一样的,就是传递的时间参数不同而已。 112 113 #### 取消控制 114 115 WithCancel