refactor(otelTrace): 替换CreateTraceableContext为CreateLinkContext

- 删除CreateTraceableContext函数,改用CreateLinkContext实现相同功能
- 修改相关测试用例,验证CreateLinkContext的上下文链路和属性
- 优化span创建逻辑,保持trace id一致,生成不同span id
- 保持上下文关联但不继承取消信号,增强追踪准确性

fix(order): 增加更新操作错误日志打印

- 在更新发送计数失败时添加详细日志输出
- 在更新池订单ID失败时添加错误日志
- 确保错误场景能够被及时监控和排查

fix(flyfishv2): 扩大发卡请求超时时间至30秒

- 将flyfishv2发卡请求的超时时间从10秒调整为30秒
- 提高接口稳定性,避免偶发超时问题

fix(flyfishv2): 调整订单状态日志信息

- 移除重复记录订单状态字段的日志内容
- 优化日志输出,突出关键字段bankOrderId

feat(scanController): 使用CreateLinkContext追踪SubmitPool流程

- 使用CreateLinkContext创建链路追踪上下文
- 添加span属性记录bankOrderId
- 在提交流程中增加事件标记StartScan和EndScan
- 确保请求链路完整便于性能监控和错误排查

fix(poolService): 优化redis错误日志及span事件处理

- 添加获取用户订单失败的错误日志
- 将acquire user order事件改为先设置属性再添加事件
- 删除无用日志调用,减少日志冗余
- 确保追踪span事件表示清晰准确

chore(deps): 更新Dockerfile代理配置秘钥

- 更新Dockerfile中proxyUrl、proxyAuthKey和proxyAuthPwd
- 修改distinct参数为true,提高代理请求正确性

style(otelTrace): 调整mustSampled数组增加SubmitOrder

- 在mustSampled列表中添加“SubmitOrder”标签
- 保持采样清单同步,确保重要操作被采样记录
This commit is contained in:
danial
2025-12-10 16:25:04 +08:00
parent 4fac3e2336
commit ab40ea48bc
10 changed files with 58 additions and 79 deletions

View File

@@ -22,9 +22,9 @@ ENV TZ=Asia/Shanghai \
shopAddr="" \
proxy="" \
proxyName="qkgo" \
proxyUrl="https://share.proxy.qg.net/get?key=7ASQH2BI&num=2&area=&isp=0&format=txt&seq=\n&distinct=false" \
proxyAuthKey="7ASQH2BI" \
proxyAuthPwd="34D6652FE7B6"
proxyUrl="https://share.proxy.qg.net/get?key=E4WS5YZV&num=2&area=&isp=0&format=txt&seq=\n&distinct=true" \
proxyAuthKey="E4WS5YZV" \
proxyAuthPwd="C474B2794C4E"
# Switch to root to perform system operations
USER root

View File

@@ -21,6 +21,7 @@ import (
"gateway/internal/service/supplier/t_mall_game"
"gateway/internal/service/supplier/third_party"
"gateway/internal/utils"
"go.opentelemetry.io/otel/attribute"
"strconv"
"strings"
"sync"
@@ -288,12 +289,11 @@ func (c *ScanController) Scan() {
return
}
otelTrace.Logger.WithContext(ctx).Info("supplierCode", zap.String("supplierCode", supplierCode))
submitPool.Go(func() {
ctx2, span2 := otelTrace.CreateLinkContext(c.Ctx.Request.Context(), "ScanController.SubmitPool")
ctx2, span2 := otelTrace.CreateLinkContext(ctx, "ScanController.SubmitPool")
defer span2.End()
span2.SetAttributes(attribute.String("bankOrderId", orderInfo.BankOrderId))
span2.AddEvent("StartScan")
scanData := supplierByCode.Scan(ctx2, orderInfo, p.RoadInfo, p.MerchantInfo)
order.InsertCardReturnDataByBankId(ctx2, orderInfo.BankOrderId, scanData.ReturnData)
span2.AddEvent("EndScan")

View File

@@ -75,7 +75,11 @@ func UpdateSendCount(ctx context.Context, orderId string) bool {
"send_count": orm.ColValue(orm.ColAdd, 1),
"is_amount_different": 1,
})
return err == nil
if err != nil {
otelTrace.Logger.WithContext(ctx).Error("update send count fail: ", zap.Error(err))
return false
}
return true
}
func UpdatePoolOrderId(ctx context.Context, orderId, poolOrderId, bankTransId string) bool {
@@ -83,7 +87,11 @@ func UpdatePoolOrderId(ctx context.Context, orderId, poolOrderId, bankTransId st
"pool_order_id": poolOrderId,
"bank_trans_id": bankTransId,
})
return err == nil
if err != nil {
otelTrace.Logger.WithContext(ctx).Error("update pool order id fail: ", zap.Error(err))
return false
}
return true
}
func UpdateOrderAmount(ctx context.Context, orderId string, orderAmount float64) bool {

View File

@@ -22,17 +22,12 @@ const (
// BatchTimeout 批量传输配置 - 优化高负载场景性能
BatchTimeout = 10 * time.Second // 批量发送间隔,平衡实时性和性能
MaxBatchSize = 1000 // 队列缓冲大小,适应突发流量
MaxExportBatchSize = 512 // 单次导出最大批量,避免单次传输过大
MaxQueueSize = 2048 // 最大队列大小,防止内存溢出
// DefaultSamplingRatio 采样配置 - 生产环境资源控制
DefaultSamplingRatio = 0.5 // 默认采样率10%,平衡观测性和性能
HighLoadSamplingRatio = 0.1 // 高负载时采样率5%,保护系统稳定性
// MaxConcurrentExports 资源限制配置 - 防止资源竞争
MaxConcurrentExports = 10 // 最大并发导出数,控制资源使用
MemoryLimitMB = 100 // 内存使用限制(MB)
DefaultSamplingRatio = 0.5 // 默认采样率50%,平衡观测性和性能
HighLoadSamplingRatio = 0.1 // 高负载时采样率10%,保护系统稳定性
)
// CircuitBreakerState 熔断器状态

View File

@@ -230,37 +230,37 @@ func TestLinkContextIndependence(t *testing.T) {
<-done
}
func TestCreateTraceableContext(t *testing.T) {
func TestCreateLinkContextBasic(t *testing.T) {
// 创建父上下文和span
ctx := context.Background()
_, parentSpan := otel.Tracer("test").Start(ctx, "ParentSpan")
defer parentSpan.End()
// 创建可追踪的上下文
traceableCtx, traceableSpan := CreateTraceableContext(ctx, "TraceableOperation")
defer traceableSpan.End()
// 创建link ctx
linkCtx, linkSpan := CreateLinkContext(ctx, "LinkOperation")
defer linkSpan.End()
// 验证traceableCtx包含父span的上下文
traceableSpanCtx := trace.SpanContextFromContext(traceableCtx)
if !traceableSpanCtx.IsValid() {
t.Error("traceableCtx应该包含有效的span上下文")
// 验证linkCtx包含父span的上下文
linkSpanCtx := trace.SpanContextFromContext(linkCtx)
if !linkSpanCtx.IsValid() {
t.Error("linkCtx应该包含有效的span上下文")
}
// 验证父span和traceable span的trace ID相同
// 验证父span和link span的trace ID相同
parentSpanCtx := parentSpan.SpanContext()
if traceableSpanCtx.TraceID() != parentSpanCtx.TraceID() {
t.Error("traceable span应该与父span有相同的trace ID")
if linkSpanCtx.TraceID() != parentSpanCtx.TraceID() {
t.Error("link span应该与父span有相同的trace ID")
}
// 验证traceable span的span ID不同
if traceableSpanCtx.SpanID() == parentSpanCtx.SpanID() {
t.Error("traceable span应该有不同的span ID")
// 验证link span的span ID不同
if linkSpanCtx.SpanID() == parentSpanCtx.SpanID() {
t.Error("link span应该有不同的span ID")
}
// 验证traceable span包含正确的属性
span := trace.SpanFromContext(traceableCtx)
// 验证link span包含正确的属性
span := trace.SpanFromContext(linkCtx)
if span == nil {
t.Error("traceableCtx应该包含有效的span")
t.Error("linkCtx应该包含有效的span")
}
// 验证span属性
@@ -270,34 +270,34 @@ func TestCreateTraceableContext(t *testing.T) {
}
}
func TestTraceableContextInGoroutine(t *testing.T) {
func TestLinkContextInGoroutineAdvanced(t *testing.T) {
// 创建父上下文和span
ctx := context.Background()
_, parentSpan := otel.Tracer("test").Start(ctx, "ParentSpan")
defer parentSpan.End()
// 在goroutine中使用traceable ctx
// 在goroutine中使用link ctx
done := make(chan bool)
go func() {
traceableCtx, traceableSpan := CreateTraceableContext(ctx, "GoroutineTraceableOperation")
defer traceableSpan.End()
linkCtx, linkSpan := CreateLinkContext(ctx, "GoroutineLinkOperation")
defer linkSpan.End()
// 验证traceableCtx在goroutine中仍然有效
traceableSpanCtx := trace.SpanContextFromContext(traceableCtx)
if !traceableSpanCtx.IsValid() {
t.Error("goroutine中的traceableCtx应该包含有效的span上下文")
// 验证linkCtx在goroutine中仍然有效
linkSpanCtx := trace.SpanContextFromContext(linkCtx)
if !linkSpanCtx.IsValid() {
t.Error("goroutine中的linkCtx应该包含有效的span上下文")
}
// 验证trace ID链接
parentSpanCtx := parentSpan.SpanContext()
if traceableSpanCtx.TraceID() != parentSpanCtx.TraceID() {
if linkSpanCtx.TraceID() != parentSpanCtx.TraceID() {
t.Error("goroutine中的span应该与父span有相同的trace ID")
}
// 验证span属性
span := trace.SpanFromContext(traceableCtx)
span := trace.SpanFromContext(linkCtx)
if span == nil {
t.Error("goroutine中的traceableCtx应该包含有效的span")
t.Error("goroutine中的linkCtx应该包含有效的span")
}
done <- true
@@ -307,20 +307,20 @@ func TestTraceableContextInGoroutine(t *testing.T) {
<-done
}
func TestTraceableContextAttributes(t *testing.T) {
func TestLinkContextAttributes(t *testing.T) {
// 创建父上下文和span
ctx := context.Background()
_, parentSpan := otel.Tracer("test").Start(ctx, "ParentSpan")
defer parentSpan.End()
// 创建可追踪的上下文
traceableCtx, traceableSpan := CreateTraceableContext(ctx, "TestOperation")
defer traceableSpan.End()
// 创建link上下文
linkCtx, linkSpan := CreateLinkContext(ctx, "TestOperation")
defer linkSpan.End()
// 验证span包含正确的属性
span := trace.SpanFromContext(traceableCtx)
span := trace.SpanFromContext(linkCtx)
if span == nil {
t.Error("traceableCtx应该包含有效的span")
t.Error("linkCtx应该包含有效的span")
}
// 验证span上下文

View File

@@ -18,6 +18,7 @@ type traceIDRatioSampler struct {
var mustSampled = []string{
"PayNotify",
"HandleSendCardTask",
"SubmitOrder",
}
// SamplingPriority 强制采样

View File

@@ -74,30 +74,6 @@ func CreateAsyncContext(ctx context.Context, operationName string) (context.Cont
)
}
// CreateTraceableContext 创建一个可追踪的上下文传递完整的trace和span信息
// 这个函数确保子ctx能够访问父ctx中的所有追踪信息
func CreateTraceableContext(ctx context.Context, operationName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
// 获取父span的上下文
parentSpanCtx := trace.SpanContextFromContext(ctx)
// 创建新的上下文,保持追踪关联但不继承取消信号
linkCtx := trace.ContextWithSpanContext(context.Background(), parentSpanCtx)
// 添加更多追踪属性
traceOpts := append(opts,
trace.WithLinks(trace.Link{SpanContext: parentSpanCtx}),
trace.WithSpanKind(trace.SpanKindInternal),
trace.WithAttributes(
attribute.String("operation", operationName),
attribute.String("parent.trace_id", parentSpanCtx.TraceID().String()),
attribute.String("parent.span_id", parentSpanCtx.SpanID().String()),
attribute.String("context_type", "traceable"),
),
)
return otel.Tracer("traceable").Start(linkCtx, operationName, traceOpts...)
}
// SetSpanError 设置span的错误状态优化错误追踪
func SetSpanError(span trace.Span, err error, message string) {
if span == nil {

View File

@@ -159,7 +159,7 @@ func (c *FlyFishV2Impl) PayNotify() {
}
if orderInfo.Status != "wait" {
otelTrace.Logger.WithContext(ctx).Error("【飞鱼】订单状态不正确", zap.String("status", orderInfo.Status), zap.String("bankOrderId", orderInfo.BankOrderId))
otelTrace.Logger.WithContext(ctx).Error("【飞鱼】订单状态不正确", zap.String("bankOrderId", orderInfo.BankOrderId))
c.Ctx.WriteString("订单已经回调")
return
}

View File

@@ -107,7 +107,7 @@ func (s *SendCardTaskTypeFlyFishV2) HandleSendCardTask(ctx context.Context, orde
SetHeader("origin", "https://apify.fkpay.online").
SetHeader("user-agent", useragent.GetUserAgentByPlatform(useragent.PlatformPhone)).
SetHeader("referer", "https://apify.fkpay.online/show.html?orderId=FY17568845864231914279").
SetTimeout(time.Second * 10).OnBeforeRequest(func(client *resty.Client, request *resty.Request) error {
SetTimeout(time.Second * 30).OnBeforeRequest(func(client *resty.Client, request *resty.Request) error {
proxy, err2 := utils.GetProxy(ctx, utils.GenerateId(), "SendCardTaskTypeFlyFishV2_cardTask")
otelTrace.Logger.WithContext(ctx).Info("获取代理", zap.String("proxy", proxy))
if err2 != nil {

View File

@@ -436,7 +436,6 @@ func (s *OrderPoolServiceImpl) matchOrdersForFaceValue(ctx context.Context, chan
// 获取用户订单
var task card_sender.SendCardTask
if err = s.redisClient.LPopUnmarshal(ctx, customerKey, &task); err != nil {
s.metrics.RecordError("redis")
otelTrace.Logger.WithContext(ctx).Error("获取用户订单失败", zap.Error(err))
// 获取用户订单失败时,将生产订单重新放回池中
if err = s.redisClient.LPush(ctx, produceKey, produceOrderItem); err != nil {
@@ -445,7 +444,8 @@ func (s *OrderPoolServiceImpl) matchOrdersForFaceValue(ctx context.Context, chan
return
}
span.AddEvent("acquire user order", trace.WithAttributes(attribute.String("bankOrderId", task.LocalOrderID)))
span.SetAttributes(attribute.String("bankOrderId", task.LocalOrderID))
span.AddEvent("acquire user order")
if task.SendCardTaskType == "" {
task.SendCardTaskType = channel
}
@@ -481,7 +481,6 @@ func (s *OrderPoolServiceImpl) matchOrdersForFaceValue(ctx context.Context, chan
// 处理发卡任务,带重试机制
var retryCount int
for retryCount < s.config.MaxRetryCount {
otelTrace.Logger.WithContext(ctx).Info("处理发卡任务", zap.String("roadUid", roadUid), zap.Float64("faceValue", faceValue), zap.Any("produceOrderItem", produceOrderItem), zap.Any("task", task))
err = sendCardTaskType.HandleSendCardTask(ctx, produceOrderItem, task)
if err == nil {