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

Saner-Lee · · 1125 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。

# context最佳实践 本内容来源于作者的学习笔记,[地址](https://github.com/Saner-Lee/note) 另外: - [redis server很好用,不是redis client](https://github.com/Saner-Lee/redigosrv) - [长尾真的很好解决](https://github.com/Saner-Lee/backup-request) 我记得当时大学时学习谭浩强老师的`C`语言时有一句话,`C`语言程序的最小组成单位是函数。而在`golang`中提到函数第一反应就是`error`,其次就是`context`。为什么这么说呢?无论`BS`还是`CS`都需要网络交互,而且在`golang`中有着天然的并发性,因此如何去管控链路上的每个节点,以及子节点如何感知链路上的事件尤为重要。 `golang`采用树结构来描述链路,在树上传递事件,该事件用来表述父节点已经终止对任务的处理了,子节点的存在是为了辅助父节点完成任务,因此当子节点接收到事件后应当终止任务的执行。 ## 你了解context树的动作吗 `context.Context`是一个接口,而且它的实现类型都有一个匿名的`context.Context`成员,比如: ```go 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`接口。其实`cancelCtx`(`WithCancel`,`WithTimeout`和`WithDeadline`的底层依赖,至于为什么后面会提到),`timerCtx`(`WithTimeout`和`WithDeadline`的底层实现),`valueCtx`(`WithValue`的底层实现)都没有全部自己实现`context.Context`中的全部方法,都使用到了上面说到的匿名成员的特性。 [here]("https://github.com/Saner-Lee/note/blob/master/go/standard%20library/context.md")是一篇源码分析,里面给出了每个成员的实现,这里给出结果。 | | 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`。 原因:有些标准库和第三方库内部在识别`context`为`nil`的时候会直接`panic`。 ### key的选择 要求:不要使用内置类型,使用自定义私有类型。 推荐:`type $key struct{}`,节省内存,方便扩展。 原因:细节5可能会导致树中的`valueCtx`可能有相同的`key`,`context`可能都是使用者传入的,开发者无法保证`key`的唯一性,因此可能会有非预期行为,因此使用定制的私有类型,保证程序的可控性。 ### value的类型 要求:传递多个信息到下游时,使用map类型,而不是创建多个节点。 原因:通常跨进程传输数据时,会传输大量的信息,如果每个值都使用一个`context`节点,会使得`context`树很臃肿,而且信息分散在各个`context`节点,因此通常将要传输的信息打包到一个`map`然后放在一个`context`节点中。 ### value的并发安全 要求:value的内容是只读的。 原因:context 是并发安全的,但是当通过 value 获取到的数据可能存在并发安全的问题,如果值的类型是指针或者引用类型,就会面临并发安全的问题。上下文用于描述历史,历史中已经存在的内容不可变更,历史只会不断的增长。 ### 超时控制 要求:使用父节点创建新的节点时,不需要考虑祖先节点的超时时间。 原因:细节4确保内部会通过节点类型变换保证父节点的`Deadline`大于子节点。

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

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

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