golang中,context.WithValue的使用规范问题

我们首先来看一个报错

  • should not use built-in type string as key for value; define your own type to avoid collisions (SA1029)go-staticcheck

代码长这样


image.png

上面这段程序的波浪线报错不是warnning级别的,可以说是编辑器的提示,虽说不影响程序的打包运行,但作为强迫症玩家,属实难以接受

这里是golang针对context.Context 类型的使用定义的规范,下面是一些官方话术的解释

  • 使用context.Context时,不应该使用内置类型作为 KV 中 key 的类型,而应该使用自定义的类型来避免冲突。
  • 当使用 context.Context 类型保存 KV 对时, key 不能使用原生类型,而应该使用派生类型

我们看一些案例

我们在项目中,通常利用 context.Context 作为一个生命周期的上下文传递,贯穿全局,经常来存一些自定义的键值对,保存新的 context 对象

ctx = context.WithValue(ctx, someKey, someValue)

WithValue方法标准库的定义为
key 和 val 都是any类型

func WithValue(parent Context, key, val any) Context {
    ……

}

该方法的注释有这么一句context keys often have concrete type struct{}
就是建议 key 的类型通常为具体的结构体类型

我们的实际使用中,大多会这么样写

ctx = context.WithValue(ctx, "openid", userOpenID)

潜在问题

现在的项目往往涉及多个包的紧密耦合和团队成员间的协作。在一个 ctx 对象的生命周期中,它需要穿越多个逻辑层或包,每个模块都有可能利用 ctx 来存储相关信息。

以用户模块为例,它可能会使用 ctx 来缓存用户的 openid 字段。这种做法本身是合理的。随后,这个 ctx(以及相应的代码逻辑)继续流转,大家默认使用这个 "openid" 键来存储数据。

然而,当一个紧急需求出现,比如需要快速开发一个群聊功能,并且尽可能地复用现有代码以减少开发工作量时,问题就出现了。可能群聊模块在利用用户模块的代码时,无意中也使用了 "openid" 这个键,这次是用来存储群主的 openid。结果,当代码运行时,支援的开发人员发现了一个奇怪的现象:群主的 openid 似乎在不断地变化,仿佛群主的身份在不断轮换。

处理办法

我们以一种常见的思维方式来处理,大家通常会说对ctx里的 key 里的内容统一规范管理,大家操作ctx时都遵循一套规则, 这的确是一个很不错的办法

但是我现在对统一规范管理,这6个字特别厌恶,动不动就统一管理的,随着岁月的流失谁还会想着去看文档,干点儿啥都去先看文档约束,麻烦

这种局部的工作细节,分而治之,尽可能避免集中式的管理显然更加适用,又不是什么大的模块

我们先来一个小小的对比优化代码

type chatGroup string

func main() {
    ctx := context.Background()
    ctx = context.WithValue(ctx, "openid", "不是群主")
    ctx = context.WithValue(ctx, chatGroup("openid"), "群主")

    fmt.Println(ctx.Value("openid"))
    fmt.Println(ctx.Value(chatGroup("openid")))
}

输出

不是群主
群主

通过chatGroup,一眼就能看出来是群聊模块的东西

我们再来简写、优化一点

type chatGroup struct{}

func main() {
    ctx := context.Background()
    ctx = context.WithValue(ctx, "openid", "不是群主")
    ctx = context.WithValue(ctx, chatGroup{}, "群主")

    fmt.Println(ctx.Value("openid"))
    fmt.Println(ctx.Value(chatGroup{}))
}

struct{} 类型(准确来说空结构体是已初始化的值)也可以作为 KV 的 key 类型,当然了,也应该定义为自定义类型。

使用 struct{} 的好处是,这个类型在 Go 中原则上是不占内存空间和 gc 开销的,可以提升性能

进阶例子

封装一个ctx 引入trace ID的案例

// traceid包 用于在 context 中维护 trace ID
package traceid

import "context"

type traceIDKey struct{}

// WithTraceID 往 context 中存入 trace ID
func WithTraceID(ctx context.Context, traceID string) context.Context {
    return context.WithValue(ctx, traceIDKey{}, traceID)
}

// TraceID 从 context 中提取 trace ID
func TraceID(ctx context.Context) string {
    v := context.Value(ctx, traceIDKey{})
    id, _ := v.(string)
    return id
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,717评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,501评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,311评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,417评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,500评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,538评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,557评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,310评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,759评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,065评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,233评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,909评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,548评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,172评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,420评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,103评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,098评论 2 352

推荐阅读更多精彩内容