You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang gRPC集成New Relic时Redis事务未上报显示问题

问题描述

我被该问题困扰已有数日:参照官方文档与同行建议配置后仍未解决。当前我使用Golang搭配gRPC框架,接入New Relic用于追踪事务与服务整体性能,目前已可正常声明普通事务与分段,但Redis相关数据库操作事务始终未在New Relic面板的数据库板块展示。我已尝试在执行Redis操作时将New Relic上下文传入Redis客户端,先后测试了后台上下文、当前事务上下文等方案,Redis查询本身可正常执行,但始终无对应监控数据上报。以下为我的实现代码示例:

1. 执行缓存更新事务的函数

// updateCache ...
func updateCached(data *statemachinepkgv1.StateMachine, sessionID string, ctx context.Context) (*statemachinepkgv1.StateMachine, error) {

    //Transaction and segment logic
    
    //this does not create a brand new relic application, just gets the main instance
    relic, err := tools.InitRelic()

    if err != nil {
        log.Fatalf("failed to create/instace newrelic.NewApplication: %v", err)
    }

    txn := relic.StartTransaction("redis", nil, nil)

    defer newrelic.StartSegment(txn, "updateCached").End()
    defer newrelic.StartSegment(tools.CurrentTransaction, "updateCached").End() //tools.CurrentTransaction has the context of main web transaction stored with singleton design

    defer txn.End()

    dataParse, err := json.Marshal(data)
    if err != nil {
        return data, err
    }

    duration, err := time.ParseDuration(os.Getenv("GO_STATEMACHINE_REDIS_TIMELIFE"))
    if err != nil {
        return data, err
    }

    //REDIS LOGIC
    dbTransaction := newrelic.FromContext(ctx)
    newRelicContext := newrelic.NewContext(context.Background(), dbTransaction)

    err = masterClient.Set(newRelicContext, sessionID, string(dataParse), duration).Err()

    if err != nil {
        return data, err
    }

    return data, nil

}

2. Redis单例实现

package tools

import (
    "context"
    "os"

    "github.com/go-redis/redis/v8"
    nrredis "github.com/newrelic/go-agent/v3/integrations/nrredis-v8"
    newrelic "github.com/newrelic/go-agent/v3/newrelic"
)

var redisOpt = &redis.Options{
    Addr:     os.Getenv("GO_STATEMACHINE_REDIS_HOST") + ":" + os.Getenv("GO_STATEMACHINE_REDIS_PORT"),
    DB:       1,
    Password: os.Getenv("GO_STATEMACHINE_REDIS_PASSWORD"),
}

var Client *redis.Client = nil
var Ctx context.Context = nil

func getTransaction() *newrelic.Transaction {
    redisTransaction := newrelic.FromContext(context.Background())
    return redisTransaction
}

func init() {

    Client = redis.NewClient(redisOpt)
    Client.AddHook(nrredis.NewHook(redisOpt))

    txn := getTransaction()
    Ctx = newrelic.NewContext(context.Background(), txn)
}

func GetRedis() (*redis.Client, context.Context) {

    if Client == nil {
        Client = redis.NewClient(redisOpt)
        Client.AddHook(nrredis.NewHook(redisOpt))

        txn := getTransaction()
        Ctx = newrelic.NewContext(context.Background(), txn)

    }

    return Client, Ctx
}

3. New Relic单例实现

package tools

import (
    "context"
    "fmt"
    "log"
    "os"

    newrelic "github.com/newrelic/go-agent"
)

var RelicApplication newrelic.Application = nil
var CurrentTransaction newrelic.Transaction = nil

func init() {

    fmt.Println("INIT RELIC CREATING AT RUNTIME")
    cfg := newrelic.NewConfig("go-vozyengine-statemachine-service", os.Getenv("NRELIC_KEY"))
    cfg.Logger = newrelic.NewDebugLogger(os.Stdout)
    cfg.BrowserMonitoring.Attributes.Enabled = true
    cfg.TransactionTracer.Enabled = true
    cfg.TransactionEvents.Enabled = true
    cfg.DistributedTracer.Enabled = true
    cfg.CustomInsightsEvents.Enabled = true
    cfg.ErrorCollector.Enabled = true
    cfg.ErrorCollector.CaptureEvents = true

    var err error
    RelicApplication, err = newrelic.NewApplication(cfg)

    if err != nil {
        log.Fatalf("failed to create newrelic.NewApplication: %v", err)
    }

    fmt.Println("INIT RELIC = RETURNING")

}

func SetSubTransaction(txn newrelic.Transaction) {
    //set new current transaction
    CurrentTransaction = txn
}

func SetSubTransactionByContext(ctx context.Context) {
    fmt.Println("SETTING SUB TRANSACTION VIA CONTEXT - default set")
    txn := newrelic.FromContext(ctx)
    SetSubTransaction(txn)
}

func InitRelic() (newrelic.Application, error) {

    fmt.Println("INIT RELIC REBUG")
    if RelicApplication == nil {

        fmt.Println("INIT RELIC = NOT INIT CREATING")
        cfg := newrelic.NewConfig("go-vozyengine-statemachine-service", "29827623658187f9e25fdde2f2fee06da906NRAL")
        cfg.Logger = newrelic.NewDebugLogger(os.Stdout)
        cfg.BrowserMonitoring.Attributes.Enabled = true

        // Enabling distributed tracing will disable the cross application tracing feature. Distributed tracing is
        //  an improved version of cross-application tracing and only one can be enabled at a time.
        cfg.DistributedTracer.Enabled = true

        RelicApplication, err := newrelic.NewApplication(cfg)

        if err != nil {
            log.Fatalf("failed to create newrelic.NewApplication: %v", err)
        }

        fmt.Println("INIT RELIC = RETURNING")

        return RelicApplication, err

    } else {

        fmt.Println("INIT RELIC = ALREADY INIT RETURNING")
        return RelicApplication, nil
    }

}

若能提供相关排查帮助,我将万分感谢。


问题根因与修复方案

Redis监控数据不上报是三个核心代码错误叠加导致的:

1. New Relic SDK版本混用

你在Redis单例中引入的是v3版本的New Relic依赖(github.com/newrelic/go-agent/v3/下的nrredis钩子、核心SDK),但New Relic单例实现用的是旧v1版本的SDK(github.com/newrelic/go-agent),两个版本的事务结构、上下文存取逻辑完全不兼容,v3版本的nrredis钩子根本读不到v1 SDK存在上下文里的事务信息。
修复方案:

  • 将New Relic单例里的所有import替换为v3版本的包,全链路统一使用v3版本的API,彻底删除旧v1版本SDK的依赖。

2. 上下文传递逻辑完全错误

  • Redis单例在init初始化阶段就从空的context.Background()取事务、生成全局静态Ctx,这个上下文里没有绑定任何活跃请求的事务,本身就是无效值。
  • 执行Redis Set命令时用的是masterClient实例,不是你添加了nrredis钩子的tools.Client实例,钩子逻辑根本不会触发。
  • 取到入参ctx里的事务后,又把事务绑定到全新的空context.Background()上再传给Redis,会丢失链路关联的元数据。
  • 用全局单例变量CurrentTransaction存事务,并发场景下会被不同请求的事务值覆盖,直接导致数据串扰、丢失。
    修复方案:
  • 删除Redis单例里提前生成、存储全局Ctx的逻辑,上下文必须随请求动态传递,不能在服务启动阶段预生成静态全局上下文。
  • 所有Redis操作统一使用添加了nrredis钩子的Client实例,禁止混用未加钩子的Redis客户端。
  • 执行Redis命令时直接传入绑定了当前请求事务的上下文,不要新建空Background上下文二次包装。
  • gRPC接口入口处拿到请求绑定的New Relic事务后,将事务存入当前请求的context,后续所有数据库、缓存操作直接透传这个请求context即可,不要用全局变量存储事务。

3. 事务重复创建、分段绑定错误

你在updateCached函数里额外新建了一个名为"redis"的独立事务,同时给这个独立事务、全局存储的主事务都绑定了同名分段,直接导致链路断裂:Redis操作的监控数据如果绑定到gRPC主事务,你新建的独立事务无法关联到这部分数据,上报时数据归属错误,不会被统计到数据库指标板块。
修复方案:

  • 不要在业务子函数里额外调用StartTransaction新建独立事务,所有分段、数据库操作全部归属到gRPC入口启动的主事务下,操作结束后正常结束对应分段即可。

内容的提问来源于stack exchange,提问作者Andrew Alizaga

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 05:27:21