V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
RedisMasterNode
V2EX  ›  Go 编程语言

Go 语言如果没有 ctx 传递,如何让 Trace ID 连贯传递呢?

  •  
  •   RedisMasterNode · 247 天前 · 2328 次点击
    这是一个创建于 247 天前的主题,其中的信息可能已经有所发展或是发生改变。

    RT 对于 Go (这类)没有 ThreadLocal 的语言,如果在服务方法调用之间缺漏了 ctx context.Context 的传递,Trace 的信息就无法传递下去。

    通常来说对于平台、工具方来说(例如做 Trace 平台的人),想推动所有业务都去改造是相当困难的,请教一下了解的 V 友有什么办法能 “改善” 这种情况?

    28 条回复    2023-09-07 12:31:12 +08:00
    juzzle
        1
    juzzle  
       247 天前
    用一个全局的 map ,key 是 goroutine 的唯一 Id ,value 是 RequestId
    RedisMasterNode
        2
    RedisMasterNode  
    OP
       247 天前
    @juzzle 我想了一下,这种方案可以在流量进来和流量出去的时候为(可能是空白的) ctx 补充 trace id ,但是印象中:
    1. goroutine 没有唯一 ID ,之前了解到是故意这样设计的,不知道这种情况后来有没有改进
    2. goroutine 之间的 trace id (可能)不容易传递

    其实更想了解有没有一些无侵入,业务不感知的方案,能够达到同 goroutine 内补全 trace id 的效果(也就是可以不考虑问题 2 )
    danbai
        3
    danbai  
       247 天前 via Android
    流量进来从堆栈拿 gid 出去再判断。这是魔法
    Nazz
        5
    Nazz  
       247 天前
    错误往上抛, 最后在中间件里面打印日志, 中间件里面可以访问 ctx 拿到 trace_id
    RedisMasterNode
        6
    RedisMasterNode  
    OP
       247 天前
    @Nazz 不是,哥们,那你这个 trace id 如何传递给下一个调用方呢?
    RedisMasterNode
        7
    RedisMasterNode  
    OP
       247 天前
    @danbai 记下魔法了,跟 1 楼其实是一样的。让我们猜猜业务方愿不愿意改 orz
    JustSong
        8
    JustSong  
       247 天前 via Android
    ctx 肯定要传啊😂
    RedisMasterNode
        9
    RedisMasterNode  
    OP
       247 天前
    @JustSong 这个东西就跟你和刚用 Redis 的人讨论可不可以使用 `KEYS` 一样的,不可能要求所有人立刻就知道这个东西重要不重要,可行与否,以及是否强制要传。等发现问题要改的时候往往是很难改得动,所以才会有很强的需求寻找如何优雅解决,而不是反推他们 “全部做对”(当然,全部做对肯定是最好最好的,不能因为难就否认)。
    qloog
        10
    qloog  
       247 天前
    如果真不想全改,可以改一些有网络请求的组件,比如数据库,http client, redis 之类的,调用方只要升级核心组件即可,其他方法有好多其实也可以不用传递,毕竟没有网络请求。如果真想全部 trace, 建议加上 ctx, 长期来看还是有好处的,长痛不如短痛。
    Nazz
        11
    Nazz  
       247 天前 via Android
    @RedisMasterNode 得传递 ctx🌚
    odirus
        12
    odirus  
       247 天前
    负责过类似的工作,手动传最稳定、可靠。要是担心的话在测试环境拦截出口、入口日志,分析是否有缺失的,有缺失的就告警出来。
    kkhaike
        13
    kkhaike  
       247 天前   ❤️ 2
    使用 golangci-lint 的 contextcheck
    https://golangci-lint.run/usage/linters/
    https://github.com/kkHAIKE/contextcheck

    这个是我写的😊
    crysislinux
        14
    crysislinux  
       247 天前
    我倾向于显示传递,其实五楼推荐的办法是合适的,业务抛出业务相关的数据。中间件里输出信息的时候带上 reqId 。但是那种业务上要传的比如 tenant id 就没办法了。显式的传好了。
    RedisMasterNode
        15
    RedisMasterNode  
    OP
       247 天前
    @odirus
    @kkhaike
    谢谢两位朋友的建议,是个可以实施并且可以慢慢推的方式,可能落地起来还需要搭配一些宣讲一点点改善
    RedisMasterNode
        16
    RedisMasterNode  
    OP
       247 天前
    @crysislinux 五楼这个是不好的解决方案,视角很局限,我们想解决的是业务间传递的问题,而对于单一的服务,如果它自己有这样的需求,它肯定有办法独立解决,向上抛错误也好,补齐 ctx 也罢。

    而在跨团队协作里面推动所有人完成一件事是比较困难的,一个单元没解决好,大家都没办法拿到最好的结果。

    另外回归到方案本身,它是不太有实践价值的,试想你现在只在 request log 显示了 trace id ,其他所有日志都没 trace id ,你如何串联所有日志?那,这个 trace id 只出现在 request log 的意义又是什么,岂不是跟没有 id 一样了。
    crysislinux
        17
    crysislinux  
       247 天前 via Android
    @RedisMasterNode 这个需求其实很普遍,一搜遍地都是,但是绝大部分框架都没提供一个方案,Java 我不是很熟悉不说了。nodejs 这边 async context 没进标准,nodejs 自己的 async hooks 可靠性不够,这个 feature 本身推进的也慢。可能业界并不觉得这是一个很值得解决的问题。。不过从业务的角度来说有这个东西确实会很方便。
    teli
        19
    teli  
       247 天前
    有个办法,拿到 goroutine 的 id ,不知道还有没有效
    xiaocaiji111
        20
    xiaocaiji111  
       246 天前
    如果想要 threadLocal 这种效果只能用魔法拿到 gid ,不过好像不太稳定
    我们开始项目小也没有考虑过 traceId ,后面项目中函数参数不管用不用的到默认第一个就是 context ,再后来做电商生态等原因回归了 java 。
    mrgeneral
        21
    mrgeneral  
       246 天前
    无它,规范耳。

    另外一个就是放在 mesh 层做,对业务透明。
    RedisMasterNode
        22
    RedisMasterNode  
    OP
       246 天前
    @mrgeneral mesh 层如何将 in & out 的流量进行关联呢?
    例如我是服务 B ,收到了一个服务 A 的请求,处理,然后向服务 C 发一个请求,mesh 的基础设施如何保证发给 C 的请求携带的 TraceID 与 A 发给我的 TraceID 一样呢?
    ensonmj
        23
    ensonmj  
       245 天前 via iPhone
    直接告诉他们你们 latency 很高,需要优化。自己去分析哪儿是瓶颈,他们就想到用了
    bli22ard
        24
    bli22ard  
       240 天前
    要有 tid ,老老实实显式传递。不单是 tid 的问题,还有 log 的问题。 最佳实战就是 ctx ,携带 log 和 tid
    paceewang1
        25
    paceewang1  
       235 天前
    之前也遇到过这个问题,用魔法取 gid 不可取,只能通过传递变量的方式进行,其实这里也有两种实现:
    1 、到底是传递 context ,很多框架用这种,但是实际的 Log 方法参数的时候要带上 ctx ,并且如果需要携带的 trace 多的话感觉效率不高
    2 、传递 log 变量呢,有一些框架用这种,第一眼看到也觉得很新奇,个人感觉用 log 变量传递的话能更高效的携带各种 trace ,并且 Log 方法参数和普通日志库都一致
    RedisMasterNode
        26
    RedisMasterNode  
    OP
       235 天前
    @paceewang1 我们之前在业务团队的时候倾向于用 ctx 传递,因为第三方库的方法签名不可控不可改,但是基本都保留了 ctx 。如果用 log 传递,或者说把 log 变量放在结构体内传递,它是对 ctx 的隐式使用,而且是 hardcode 的使用,无法分离出来给第三方方法用(或者说成本不如方法 1 )

    而这个帖子,其实不是想讨论如何改成传递 ctx ,而是说不发生任何业务修改的情况下如何拦截采集这种信息。因为想改动的人不是业务团队,也就是不是写这个代码的人,而是平台提供方,如何在极低成本(例如,只修改 cmd/main.go 方法里面的一两行)的目标下完成改造,(尽可能)让所有调用、日志都携带 trace 信息。

    另外我们之前选用方法 1 做业务层面的改造时,log.Info(string) 改成了 log.Info(context.Context, string),这个没啥好办法,都是全局的字符串替换修改调用方的,修改成了 context.TODO() 传入,然后等未来慢慢补全,实际落地下来感觉效果还可以的,而且有利于整体习惯养成。用 log 传递那是不是又不用设计 ctx 在方法签名里了?实际上这在 go 里是个不好的实践,相当于用 log 传递的方式,节约了 log.Info 调用的改造成本,但是继续保留了不传 ctx 的坏习惯,个人认为不可取。
    kingofzihua
        27
    kingofzihua  
       233 天前
    @RedisMasterNode #26 你看下 https://skywalking.apache.org/docs/skywalking-go/next/en/concepts-and-designs/key-principles/ 能否满足你的要求,看文档 skywalking go 使用起来貌似没有需要业务方做大量的改动,你你参考下他的实现
    RedisMasterNode
        28
    RedisMasterNode  
    OP
       233 天前
    @kingofzihua 很高兴在这里看到有人提及 skywalking go ,我也是其中的一位 contributor ,我觉得混合编译及代码生成是个很有创意的 idea ,因为项目比较新,短期内也没办法直接推广到公司用,未来看看其他使用方的反馈
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2666 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 15:34 · PVG 23:34 · LAX 08:34 · JFK 11:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.