Go语言接入
概述
本文介绍Go语言的应用接入流程,点击“应用性能监控->应用列表->新建应用”进入到接入应用页面。

接入流程
点击进入到接入应用页面,可切换语言查看接入流程,按照流程接入后即可在“应用性能监控->应用列表”显示接入的应用

OpenTelemetry
步骤 1:获取接入点和鉴权
注:接入点和鉴权信息根据地域和具体用户返回,准确内容请查看控制台
- 接入点:apm-collector.bj.baidubce.com
- Authentication:UFSpMM***lnFrVBqtPDK
步骤 2:引入 OpenTelemetry 相关依赖,实现 SDK 初始化逻辑
1import (
2 "context"
3 "errors"
4 "go.opentelemetry.io/contrib/instrumentation/runtime"
5 "go.opentelemetry.io/otel"
6 "go.opentelemetry.io/otel/attribute"
7 "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
8 "go.opentelemetry.io/otel/exporters/otlp/otlptrace"
9 "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
10 "go.opentelemetry.io/otel/propagation"
11 "go.opentelemetry.io/otel/sdk/metric"
12 "go.opentelemetry.io/otel/sdk/resource"
13 sdktrace "go.opentelemetry.io/otel/sdk/trace"
14 "log"
15 "os"
16)
17
18func setupOTelSDK(ctx context.Context) (shutdown func(context.Context) error, err error) {
19 os.Setenv("OTEL_GO_X_DEPRECATED_RUNTIME_METRICS", "1")
20 var shutdownFuncs []func(context.Context) error
21
22 shutdown = func(ctx context.Context) error {
23 var err error
24 for _, fn := range shutdownFuncs {
25 err = errors.Join(err, fn(ctx))
26 }
27 shutdownFuncs = nil
28 return err
29 }
30
31 headers := map[string]string{"Authentication": "<Authentication>"} // <Authentication>替换为业务系统Authentication
32 endpoint := "<endpoint>" // <endpoint> 填写上报地址,注意需要省略 http:// 前缀
33 serviceName := "<servicename>" // <serviceName>替换为应用名
34 hostName := "<hostName>" // <hostName>替换为IP地址
35
36 // 创建 Trace Exporter
37 opts := []otlptracehttp.Option{
38 otlptracehttp.WithHeaders(headers),
39 otlptracehttp.WithEndpoint(endpoint),
40 otlptracehttp.WithInsecure(),
41 }
42 traceClient := otlptracehttp.NewClient(opts...)
43 r, err := resource.New(ctx, []resource.Option{
44 resource.WithAttributes(
45 attribute.KeyValue{Key: "service.name", Value: attribute.StringValue(serviceName)},
46 attribute.KeyValue{Key: "host.name", Value: attribute.StringValue(hostName)},
47 attribute.KeyValue{Key: "telemetry.sdk.language", Value: attribute.StringValue("go")},
48 ),
49 }...)
50 if err != nil {
51 log.Fatal(err)
52 }
53 traceExp, err := otlptrace.New(ctx, traceClient)
54 if err != nil {
55 log.Fatal(err)
56 }
57 bsp := sdktrace.NewBatchSpanProcessor(traceExp)
58 tp := sdktrace.NewTracerProvider(
59 sdktrace.WithSampler(sdktrace.AlwaysSample()),
60 sdktrace.WithSpanProcessor(bsp),
61 sdktrace.WithResource(r),
62 )
63 otel.SetTracerProvider(tp)
64 otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
65
66 // 创建 Metric Exporter
67 metricExp, err := otlpmetrichttp.New(
68 ctx,
69 otlpmetrichttp.WithEndpoint(endpoint),
70 otlpmetrichttp.WithHeaders(headers),
71 otlpmetrichttp.WithInsecure(),
72 )
73 if err != nil {
74 log.Fatal(err)
75 }
76
77 reader := metric.NewPeriodicReader(metricExp, metric.WithProducer(runtime.NewProducer()))
78 mp := metric.NewMeterProvider(metric.WithReader(reader), metric.WithResource(r))
79 otel.SetMeterProvider(mp)
80
81 if err := runtime.Start(); err != nil {
82 log.Fatal(err)
83 }
84 return
85}
参数说明如下:
| 参数名称 | 描述 | 示例值 |
| --- | --- | --- |
| <serviceName> | 应用名,多个使用相同 serviceName 接入的应用进程,在 APM 中会表现为相同应用下的多个实例。 | csm |
| <Authentication> | 步骤 1 中拿到的业务系统 Authentication。 | ItEpH7MlHT7okHCrVv9TSFsE |
| <hostName> | 该实例的主机名,是应用实例的唯一标识,通常情况下可以设置为应用实例的 IP 地址。 | 192.168.1.1 或 my-instance-hostname |
| <endpoint> | 步骤 1 中拿到的接入点。 | apm-collector.bj.baidubce.com |
步骤 3:SDK 初始化,启动 HTTP 服务
1func main() {
2 if err := run(); err != nil {
3 log.Fatalln(err)
4 }
5}
6
7func run() (err error) {
8 ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
9 defer stop()
10
11 // 初始化 SDK
12 otelShutdown, err := setupOTelSDK(ctx)
13 if err != nil {
14 return
15 }
16 // 优雅关闭
17 defer func() {
18 err = errors.Join(err, otelShutdown(context.Background()))
19 }()
20
21 // 启动HTTP服务
22 srv := &http.Server{
23 Addr: ":8080",
24 BaseContext: func(_ net.Listener) context.Context { return ctx },
25 ReadTimeout: time.Second,
26 WriteTimeout: 10 * time.Second,
27 Handler: newHTTPHandler(),
28 }
29 srvErr := make(chan error, 1)
30 go func() {
31 srvErr <- srv.ListenAndServe()
32 }()
33
34 select {
35 case err = <-srvErr:
36 return
37 case <-ctx.Done():
38 stop()
39 }
40
41 err = srv.Shutdown(context.Background())
42 return
43}
步骤 4: 对 HTTP 接口进行埋点增强
1func newHTTPHandler() http.Handler {
2 mux := http.NewServeMux()
3
4 handleFunc := func(pattern string, handlerFunc func(http.ResponseWriter, *http.Request)) {
5 // 对HTTP路由进行埋点
6 handler := otelhttp.WithRouteTag(pattern, http.HandlerFunc(handlerFunc))
7 mux.Handle(pattern, handler)
8 }
9
10 // 注册接口
11 handleFunc("/simple", simpleIOHandler)
12
13 // 对所有接口进行埋点增强
14 handler := otelhttp.NewHandler(mux, "/")
15 return handler
16}
17
18func simpleIOHandler(w http.ResponseWriter, r *http.Request) {
19 io.WriteString(w, "ok")
20}
步骤5:接入验证
启动 Go 应用后,通过 8080 端口访问对应的接口,例如 curl -X POST http://localhost:8080/simple -H "Content-Type: application/json",应用就会向 APM 上报处理 HTTP 请求相关的链路数据。在有正常流量的情况下,<应用性能监控>→<应用列表>中将展示接入的应用。由于数据的处理存在一定延时,如果接入后在控制台没有查询到应用,请等待 30 秒左右。
Jaeger
步骤 1:获取接入点和鉴权
注:接入点和鉴权信息根据地域和具体用户返回,准确内容请查看控制台
- 接入点:apm-collector.bj.baidubce.com
- Authentication:UFSpMM***lnFrVBqtPDK
1#### 步骤2 :编程式接入
2
3* 首先通过如下命令获取依赖
4
5```echarts-json
6go get github.com/uber/jaeger-client-go
- 创建Tracer对象
1func initTracer() (opentracing.Tracer, func()) {
2 cfg := jaegercfg.Configuration{
3 ServiceName: "service-A", // 应用名
4 Sampler: &jaegercfg.SamplerConfig{
5 Type: jaeger.SamplerTypeConst,
6 Param: 1,
7 },
8 Reporter: &jaegercfg.ReporterConfig{
9 LogSpans: true,
10 CollectorEndpoint: "http://apm-collector.bj.baidubce.com/api/traces",
11 HTTPHeaders: map[string]string{
12 "Authorization": "Bearer W9q5g**********ID2G", // 替换真实 token
13 },
14 },
15 }
16
17 tracer, closer, err := cfg.NewTracer(
18 jaegercfg.Logger(jaeger.StdLogger),
19 )
20 if err != nil {
21 panic(fmt.Sprintf("ERROR: cannot init Jaeger: %v\n", err))
22 }
23
24 return tracer, func() { closer.Close() }
25}
- 下游服务中手动注入
1// simulateBSpan 在 A 服务里模拟 B 服务调用
2func simulateBSpan(ctx context.Context, tracer opentracing.Tracer) {
3 // 创建 B 的子 span
4 span, _ := opentracing.StartSpanFromContext(ctx, "service-B-span")
5 defer span.Finish()
6
7 // 模拟 B 内部业务耗时
8 time.Sleep(150 * time.Millisecond)
9}
- mock 调用
1func main() {
2 tracer, closer := initTracer()
3 defer closer()
4
5 // 创建 A 服务 root span
6 rootSpan := tracer.StartSpan("service-A-root")
7 ctx := opentracing.ContextWithSpan(context.Background(), rootSpan)
8
9 // 模拟调用 B 服务
10 simulateBSpan(ctx, tracer)
11
12 // 模拟 A 服务业务处理
13 time.Sleep(200 * time.Millisecond)
14 rootSpan.Finish()
15
16 // 等待 reporter flush 完成
17 time.Sleep(3 * time.Second)
18}
更多详细使用方法,请参见jaeger-client-go 文档
步骤3:重启验证
重启编译好的GO应用后,即可开始向APM上传链路数据,在有正常流量的情况下,应用性能监控 > 应用列表 中将展示接入的应用。由于数据的处理存在一定延时,如果接入后在控制台没有查询到应用,请等待 30 秒左右。
完整Demo
1go
2package main
3
4import (
5 "context"
6 "fmt"
7 "time"
8
9 "github.com/opentracing/opentracing-go"
10 jaegercfg "github.com/uber/jaeger-client-go/config"
11 "github.com/uber/jaeger-client-go"
12)
13
14func initTracer() (opentracing.Tracer, func()) {
15 cfg := jaegercfg.Configuration{
16 ServiceName: "service-A",
17 Sampler: &jaegercfg.SamplerConfig{
18 Type: jaeger.SamplerTypeConst,
19 Param: 1,
20 },
21 Reporter: &jaegercfg.ReporterConfig{
22 LogSpans: true,
23 CollectorEndpoint: "http://apm-collector.bj.baidubce.com/api/traces",
24 HTTPHeaders: map[string]string{
25 "Authorization": "Bearer xxxxx", // 替换为真实 token
26 },
27 },
28 }
29
30 tracer, closer, err := cfg.NewTracer(
31 jaegercfg.Logger(jaeger.StdLogger),
32 )
33 if err != nil {
34 panic(fmt.Sprintf("ERROR: cannot init Jaeger: %v\n", err))
35 }
36
37 return tracer, func() { closer.Close() }
38}
39
40// simulateBSpan 在 A 服务里模拟 B 服务调用
41func simulateBSpan(ctx context.Context, tracer opentracing.Tracer) {
42 // 创建 B 的子 span
43 span, _ := opentracing.StartSpanFromContext(ctx, "service-B-span")
44 defer span.Finish()
45
46 // 模拟 B 内部业务耗时
47 time.Sleep(150 * time.Millisecond)
48}
49
50func main() {
51 tracer, closer := initTracer()
52 defer closer()
53
54 // 创建 A 服务 root span
55 rootSpan := tracer.StartSpan("service-A-root")
56 ctx := opentracing.ContextWithSpan(context.Background(), rootSpan)
57
58 // 模拟调用 B 服务
59 simulateBSpan(ctx, tracer)
60
61 // 模拟 A 服务业务处理
62 time.Sleep(200 * time.Millisecond)
63
64 rootSpan.Finish()
65
66 fmt.Println("Trace finished, waiting for flush...")
67
68 // 等待 reporter flush 完成
69 time.Sleep(3 * time.Second)
70}
Skywalking
步骤 1:获取接入点和鉴权
注:接入点和鉴权信息根据地域和具体用户返回,准确内容请查看控制台
- 接入点:apm-collector.bj.baidubce.com
- Authentication:UFSpMM***lnFrVBqtPDK
步骤2:下载Agent
前往 Skywalking Agent 下载页,找到 Go Agent ,选择右下角 Distribution,选择 tar 格式的Agent安装包,文件后缀名为 tgz。
解压后在 bin 目录下获取Agent文件,可根据当前操作系统选择对应的Agent。例如在 Linux 系统,Agent 文件为 skywalking-go-agent-.X.X-linux-amd64。
步骤3:安装Agent
SkyWalking Go 提供了 2 种安装方式,可以选择任一种方式安装:
- 无侵入式注入
对已有 Go 程序进行静态代码注入,无需修改源码,自动生成带 Agent 逻辑的版本。执行命令如下:
1path/to/skywalking-go-agent -inject path/to/your/project
其中 path/to/your/project 为您的GO项目主目录。
- 编程式接入
首先通过如下命令获取依赖:
1go get github.com/apache/skywalking-go
然后在mian.go 中引入该依赖:
1import (
2 _ "github.com/apache/skywalking-go"
3)
步骤4 修改自定义配置
获取 Skywalking 默认配置,保存为本地文件,可以命名为 config.yaml。
需要修改三处配置保证可以推送成功,其他配置可根据您的需求进行修改:
1agent:
2 service_name: '${your-service-name}' # ${your-service-name} 替换为您的应用服务名
3
4reporter:
5 grpc:
6 backend_service: '${endpoint}' # ${endpoint} 替换为上报地址,即步骤1中接入点
7 authentication: '${authentication}' # ${authentication} 替换为您步骤1中获取的Token
步骤5:编译项目
编译GO项目时添加如下参数:
1-toolexec="path/to/skywalking-go-agent -config path/to/config.yaml"
path/to/skywalking-go-agent 为您下载的Agent, path/to/config.yaml为您修改好的配置。
如下为完整的编译命令,这里假设产出应用名为app:
1go build -toolexec="path/to/skywalking-go-agent -config path/to/config.yaml" -a -o app .
步骤6:重启验证
重启编译好的GO应用后,即可开始向APM上传链路数据,在有正常流量的情况下,应用性能监控 > 应用列表 中将展示接入的应用。由于数据的处理存在一定延时,如果接入后在控制台没有查询到应用,请等待 30 秒左右。
评价此篇文章
