关于context这里一定有你不知道的细节

Saner-Lee · 2021-08-26 17:32:20 · 446215 次点击 · 大约8小时之前 开始浏览    置顶
这是一个创建于 2021-08-26 17:32:20 的主题,其中的信息可能已经有所发展或是发生改变。

context最佳实践

本内容来源于作者的学习笔记,地址

另外:

我记得当时大学时学习谭浩强老师的C语言时有一句话,C语言程序的最小组成单位是函数。而在golang中提到函数第一反应就是error,其次就是context。为什么这么说呢?无论BS还是CS都需要网络交互,而且在golang中有着天然的并发性,因此如何去管控链路上的每个节点,以及子节点如何感知链路上的事件尤为重要。

golang采用树结构来描述链路,在树上传递事件,该事件用来表述父节点已经终止对任务的处理了,子节点的存在是为了辅助父节点完成任务,因此当子节点接收到事件后应当终止任务的执行。

你了解context树的动作吗

context.Context是一个接口,而且它的实现类型都有一个匿名的context.Context成员,比如:

type cancelCtx struct {
    Context

    mu       sync.Mutex            // protects following fields
    done     chan struct{}         // created lazily, closed by first cancel call
    children map[canceler]struct{} // set to nil by the first cancel call
    err      error                 // set to non-nil by the first cancel call
}

由于匿名成员的特性,因此就算cancelCtx没有定义任何方法,那么它也已经实现了context.Contetx接口。其实cancelCtxWithCancelWithTimeoutWithDeadline的底层依赖,至于为什么后面会提到),timerCtxWithTimeoutWithDeadline的底层实现),valueCtxWithValue的底层实现)都没有全部自己实现context.Context中的全部方法,都使用到了上面说到的匿名成员的特性。

here是一篇源码分析,里面给出了每个成员的实现,这里给出结果。

Deadline Value Done Err
根节点
cancelCtx × ×
timerCtx × × ×
valueCtx × × ×

除了上面这个表格,你还需要知道关于每个节点的一些细节:

  1. 根节点的所有方法返回值都是返回参数的零值
  2. 当对节点X执行cancel方法时
    1. X节点为根的整棵树脱离context
    2. X节点为根的整棵树上所有的"cancelCtx"
      1. 通道都会被关闭
      2. err都会被赋值
    3. X节点为根的整棵树上所有的节点Done方法会立即接受到事件
  3. timerCtx本质上个还是cancelCtx,通过goroutine自动调用cancel
  4. 在创建timerCtx的时候,如果指定的时间小于等于当前父节点Deadline结果则会转变为cancelCtx
  5. 只要调用WithValue就会生成一个valueCtx

在知道了这些细节和上述表格后你就能解决使用context的所有疑问。

最佳实践

nil context

要求:不要将nil赋值给context.Context

原因:有些标准库和第三方库内部在识别contextnil的时候会直接panic

key的选择

要求:不要使用内置类型,使用自定义私有类型。

推荐:type $key struct{},节省内存,方便扩展。

原因:细节5可能会导致树中的valueCtx可能有相同的keycontext可能都是使用者传入的,开发者无法保证key的唯一性,因此可能会有非预期行为,因此使用定制的私有类型,保证程序的可控性。

value的类型

要求:传递多个信息到下游时,使用map类型,而不是创建多个节点。

原因:通常跨进程传输数据时,会传输大量的信息,如果每个值都使用一个context节点,会使得context树很臃肿,而且信息分散在各个context节点,因此通常将要传输的信息打包到一个map然后放在一个context节点中。

value的并发安全

要求:value的内容是只读的。

原因:context 是并发安全的,但是当通过 value 获取到的数据可能存在并发安全的问题,如果值的类型是指针或者引用类型,就会面临并发安全的问题。上下文用于描述历史,历史中已经存在的内容不可变更,历史只会不断的增长。

超时控制

要求:使用父节点创建新的节点时,不需要考虑祖先节点的超时时间。

原因:细节4确保内部会通过节点类型变换保证父节点的Deadline大于子节点。


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

446215 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传