flyu518 opened a new issue, #3240:
URL: https://github.com/apache/dubbo-go/issues/3240

   ### ✅ 验证清单
   
   - [x] 🔍 我已经搜索过 [现有 
Issues](https://github.com/apache/dubbo-go/issues),确信这不是重复问题
   
   ### 🚀 Go 版本
   
   1.25.5
   
   ### 📦 Dubbo-go 版本
   
   v3.3.0
   
   ### 🖥️ 服务端配置
   
   Dubbo-go, v3.3.0
   
   ### 💻 客户端配置
   
   Dubbo-go, v3.3.0
   
   ### 🌐 协议配置
   
   Triple
   
   ### 📋 注册中心配置
   
   Nacos
   
   ### 💾 操作系统
   
   🐧 Linux
   
   ### 📝 Bug 描述
   
   ### 问题描述
   
   使用内置 `otelClientTrace` filter + Triple 协议时,客户端发出的 `traceparent` 会被破坏,导致下游 
span 的 parent 指向错误。
   
   ### 根因分析
   
   **根因 1 — `parseAttachments` 覆盖了 OTEL 注入的 `traceparent`**
   
   `otelClientTrace` filter 在 filter chain 中正确创建 span 并将 `traceparent` 注入到 
`invocation.Attachments()`。但随后请求进入 protocol invoker,`triple_invoker` 内部的 
`parseAttachments` 把 `ctx.Value(constant.AttachmentKey)` 中的 attachments 再次复制到 
invocation。这个 map 里仍然保留着**入站请求带来的上游 traceparent**,直接覆盖了 OTEL 刚注入的正确值。
   
   相关源码:`protocol/triple/triple_invoker.go` → `parseAttachments` 方法
   
   **根因 2 — `AppendToOutgoingContext` 修改共享 header map,导致 `traceparent` 累积**
   
   同文件中的 `mergeAttachmentToOutgoing` 方法调用 `tri.AppendToOutgoingContext`(位于 
`protocol/triple/triple_protocol/header.go`),该函数通过 `header.Add` 操作的是 ctx chain 
中共享的**同一个** `http.Header` 指针(`extraDataKey`)。当同一个请求内串行发起多个下游 RPC 时:
   - 第 1 次调用将 `traceparent` 写入共享 header
   - 第 2 次调用通过 `header.Add` **追加**新的 `traceparent`
   - 接收方的 `OTEL propagator` 通过 `http.Header.Get("traceparent")` 提取 `trace 
context`,该方法返回 `header` 中的第一个值,即第 1 次 RPC 残留的旧 `traceparent`,而非当前调用注入的正确值。
   
   导致 span parent 指向错误的调用。
   
   相关源码:
   - `protocol/triple/triple_invoker.go` → `mergeAttachmentToOutgoing` 方法
   - `protocol/triple/triple_protocol/header.go` → `AppendToOutgoingContext` 函数
   
   
   ### 🔄 重现步骤
   
   1. 服务 A 收到请求(入站 header 包含 `traceparent`)
   2. 服务 A **串行**发起两个下游 Triple RPC,分别调用服务 B 和服务 C
   3. 在 Jaeger 等追踪系统中观察:
      - 服务 C 的调用 span 错误地显示服务 B 的 client span 为其 parent(根因 2)
      - 或者 span 引用的是上游调用方的 span,而非服务 A 的 client span(根因 1)
   
   
   ### ✅ 预期行为
   
   每个出站 RPC 应携带引用**当次调用**对应 span 的 `traceparent`,而非上游的旧值或前一次调用的残留值。
   
   
   ### ❌ 实际行为
   
   1. 服务 A 收到请求(入站 header 包含 `traceparent`)
   2. 服务 A **串行**发起两个下游 Triple RPC,分别调用服务 B 和服务 C
   3. 在 Jaeger 等追踪系统中观察:
      - 服务 C 的调用 span 错误地显示服务 B 的 client span 为其 parent(根因 2)
      - 或者 span 引用的是上游调用方的 span,而非服务 A 的 client span(根因 1)
   
   
   ### 💡 可能的解决方案
   
   ### 修复建议
   
   在 `triple_invoker.go` 中:
   1. `parseAttachments` 不应将 `traceparent` / `tracestate` 从 ctx attachments 复制到 
invocation(或让 OTEL filter 在 parseAttachments 之后执行)
   2. `mergeAttachmentToOutgoing` 应使用 `tri.NewOutgoingContext(ctx, 
http.Header{})` 替代 `tri.AppendToOutgoingContext`,避免 mutate 共享的 header map;或在写入前 
clone header
   
   ### 临时规避方案
   
   通过 `extension.SetFilter` 覆盖 `otelClientTrace` filter,在 span 创建之前清理 ctx 中的 
trace headers 并重置出站 header:
   
   <details>
   <summary>规避方案代码</summary>
   
   ```go
   // filter/otel/trace/filter.go
   func (f *otelClientFilter) Invoke(ctx context.Context, invoker 
protocol.Invoker, invocation protocol.Invocation) protocol.Result {
      // 修复1:清理 ctx 中的 trace headers,防止 parseAttachments 覆写 OTEL 注入的正确值
        attachments := make(map[string]any)
        if src, ok := ctx.Value(constant.AttachmentKey).(map[string]any); ok {
                for k, v := range src {
                        attachments[k] = v
                }
        }
        delete(attachments, "traceparent")
        delete(attachments, "tracestate")
        ctx = context.WithValue(ctx, constant.AttachmentKey, attachments)
   
        // 修复2:重置出站 triple header,防止 AppendToOutgoingContext 在共享 map 上累积
        // 前一次 RPC 调用的 traceparent 到当前调用中
        ctx = tri.NewOutgoingContext(ctx, http.Header{})
   
      // 下面是原先逻辑
      tracer := f.TracerProvider.Tracer(
                constant.OtelPackageName,
                
oteltrace.WithInstrumentationVersion(constant.OtelPackageVersion),
        )
      // ...
   }
   ```
   
   </details>
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to