上下文传播
编辑上下文传播编辑
在 Go 中,context 用于在调用链中传播请求范围的值,可能跨越 goroutine 和进程。对于基于 net/http
的服务器,每个请求都包含一个独立的上下文对象,这允许添加特定于该特定请求的值。
当您启动事务时,可以使用 apm.ContextWithTransaction 将其添加到上下文对象中。此上下文对象稍后可以传递到 apm.TransactionFromContext 以获取事务,或传递到 apm.StartSpan 以启动跨度。
创建和传播跨度的最简单方法是使用 apm.StartSpan,它接受一个上下文并返回一个跨度。该跨度将被创建为最近添加到此上下文的跨度的子级,或如上所述添加到上下文的交易。如果上下文既不包含事务也不包含跨度,则该跨度将被丢弃(即不会报告给 APM 服务器)。
例如,以一个简单的 CRUD 类型 Web 服务为例,它通过 HTTP 接受请求,然后执行相应的数据库查询。对于每个传入请求,将自动启动一个事务并将其添加到请求上下文中。此上下文需要手动传递到处理程序中的方法调用中,以便在该事务中创建跨度,例如,测量 SQL 查询的持续时间。
import ( "net/http" "go.elastic.co/apm/v2" "go.elastic.co/apm/module/apmhttp/v2" "go.elastic.co/apm/module/apmsql/v2" _ "go.elastic.co/apm/module/apmsql/v2/pq" ) var db *sql.DB func init() { // apmsql.Open wraps sql.Open, in order // to add tracing to database operations. db, _ = apmsql.Open("postgres", "") } func main() { mux := http.NewServeMux() mux.HandleFunc("/", handleList) // apmhttp.Wrap instruments an http.Handler, in order // to report any request to this handler as a transaction, // and to store the transaction in the request's context. handler := apmhttp.Wrap(mux) http.ListenAndServe(":8080", handler) } func handleList(w http.ResponseWriter, req *http.Request) { // By passing the request context down to getList, getList can add spans to it. ctx := req.Context() getList(ctx) ... } func getList(ctx context.Context) ( // When getList is called with a context containing a transaction or span, // StartSpan creates a child span. In this example, getList is always called // with a context containing a transaction for the handler, so we should // expect to see something like: // // Transaction: handleList // Span: getList // Span: SELECT FROM items // span, ctx := apm.StartSpan(ctx, "getList", "custom") defer span.End() // NOTE: The context object ctx returned by StartSpan above contains // the current span now, so subsequent calls to StartSpan create new // child spans. // db was opened with apmsql, so queries will be reported as // spans when using the context methods. rows, err := db.QueryContext(ctx, "SELECT * FROM items") ... rows.Close() }
上下文可以具有关联的截止日期,并且可以显式取消。在某些情况下,您可能希望将跟踪上下文(父事务/跨度)传播到某些代码,而无需传播取消。例如,HTTP 请求的上下文将在客户端连接关闭时取消。您可能希望在请求处理程序中执行某些操作,而不会因客户端连接关闭而取消,例如在即发即弃操作中。为了处理这种情况,我们提供了函数 apm.DetachedContext。
func handleRequest(w http.ResponseWriter, req *http.Request) { go fireAndForget(apm.DetachedContext(req.Context())) // After handleRequest returns, req.Context() will be canceled, // but the "detached context" passed into fireAndForget will not. // Any spans created by fireAndForget will still be joined to // the handleRequest transaction. }