之前利用 Istio 自带的链路追踪功能将微服务间的网络调用上报到 jaeger 平台,但无法做到函数级别的上报,需要在业务代码里自行接入客户端进行上报。
我们服务语言主要2个:golang, nodejs
所以接下来主要介绍它们如何接入。
Jaeger 介绍
Jaeger 是 Uber 开源的一个链路追踪服务组件,可以接受 opentelemetry trace 数据,并展示完整链路。之前官方提供了单独的 jaeger client sdk,不过目前官方已经不推荐,官方欢迎接入 opentelemetry 标准,所以目前使用 opentelemetry 提供的 sdk 即可接入。
Jaeger 提供了 grpc, http 2种数据上报方式,端口分别为 4317 和 4318,这里我们选了 grpc 方式。
Golang 服务接入
- 安装 opentelemetry 依赖包
go get go.opentelemetry.io/otel@v1.29.0
go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@v1.29.0
go get go.opentelemetry.io/otel/sdk@v1.29.0
go get go.opentelemetry.io/otel/trace@v1.29.0
-
初始化 Provider ,并返回全局 tracer
- 先初始化 grpc exporter, jaeger 服务的地址是 192.168.1.10:4317
- 设置全局 Provider;provider 是链路追踪的核心,用于管理 tracer,span context上下文,集成exporter
- 获取全局的 tracer;tracer 用于生成 span,每个 span 代表一段独立的时间片段
package tracing
import (
"context"
"log"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/sdk/resource"
"go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
oTrace "go.opentelemetry.io/otel/trace"
)
const TRACER_NAME = "go-service"
func NewGrpcProviderTracer() (oTrace.Tracer, func()) {
ctx := context.Background()
exp, err := otlptracegrpc.New(ctx,
otlptracegrpc.WithEndpoint("192.168.1.10:4317"),
otlptracegrpc.WithInsecure())
if err != nil {
panic(err)
}
tp := trace.NewTracerProvider(trace.WithBatcher(exp), trace.WithResource(resource.NewSchemaless(semconv.ServiceName(TRACER_NAME))))
otel.SetTracerProvider(tp)
tr := tp.Tracer(TRACER_NAME)
return tr, func() {
if err := tp.Shutdown(ctx); err != nil {
log.Printf("failed to shutdown TracerProvider: %v", err)
}
}
}
- 函数级别上报使用
func HelloBiz(ctx context.Context) {
ctx, span := tr.Start(ctx, "HelloBiz") // tr 是上面👆获取到的全局 tracer
defer span.End()
// ....
}
Nodejs 服务接入
- 修改 package.json 的 dependencies 添加 opentelemetry 依赖,然后执行
pnpm i进行安装更新
"dependencies": {
"@opentelemetry/exporter-trace-otlp-grpc": "^0.56.0",
"@opentelemetry/instrumentation-grpc": "^0.56.0",
"@opentelemetry/sdk-node": "^0.56.0",
"@opentelemetry/sdk-trace-base": "^1.29.0",
"@opentelemetry/semantic-conventions": "^1.28.0",
},
- 一样初始化好 grpc exporter,如果你的当前微服务也是用的 grpc 协议,那么使用 GrpcInstrumentation 可以注册采集请求的 trace 详情;如果你用的是 http 协议,请改用 HttpInstrumentation (需要下载对应的 npm 依赖包)
import { credentials } from '@grpc/grpc-js';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';
import { GrpcInstrumentation } from '@opentelemetry/instrumentation-grpc';
import { NodeSDK } from '@opentelemetry/sdk-node';
const serviceName = 'nodejs-service';
async function initTracer() {
const exporter = new OTLPTraceExporter({
credentials: credentials.createInsecure(),
url: 'http://192.168.1.10:4317',
});
const otelSDK = new NodeSDK({
serviceName,
spanProcessors: [new tracing.BatchSpanProcessor(exporter)],
instrumentations: [new GrpcInstrumentation()],
});
await otelSDK.start();
['SIGINT', 'SIGTERM'].forEach((signal) => {
process.on(signal, () => otelSDK.shutdown().catch(console.error).finally(() => process.exit(0)));
});
}
- 在服务启动 bootstrap 前,需要先执行上面👆的 initTracer 方法
async function bootstrap() {
await initTracer();
// ...
app.listen();
}
bootstrap()
- 函数级别上报 trace
import { api } from '@opentelemetry/sdk-node';
const serviceName = 'nodejs-service';
function SayHi() {
const tr = api.trace.getTracer(serviceName);
const span = tr.startSpan('SayHi');
// Do something
span.end();
}
效果如下:
好了,上述便是我这期分析的内容,希望对你有帮助,我们有缘再见