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