1、问题
A调用B,B调用C,它们采样率不一样,一个trace中是否会只包含部分服务的Span(比如缺少B的)?
答案:不会的。
2、解释
当A节点接收到一个请求,这个请求没有包含trace信息,jaeger会产生一个新的trace,生成一个新的traceId,同时根据当前的采样策略决定是否采样。这个采样结果会传递给B和C,B和C不需要自己进行采样判断,使用A传的值即可。
https://www.jaegertracing.io/docs/1.14/sampling/
3、验证
span生成
span := tracer.StartSpan("say-hello")
span创建
tracer.go:startSpanWithOptions创建span,并将sample设置到flag中。
var samplerTags []Tag
newTrace := false
if !isSelfRef {
if !hasParent || !parent.IsValid() {
// 需要新建trace场景
newTrace = true
// 概率采样策略使用traceID低位判断是否采样
ctx.traceID.Low = t.randomID()
if t.options.gen128Bit {
ctx.traceID.High = t.options.highTraceIDGenerator()
}
ctx.spanID = SpanID(ctx.traceID.Low)
ctx.parentID = 0
ctx.flags = byte(0)
// 一定有父节点,但是父节点的trace不合法,用户需要在httpheader设置jaeger-debug-id
if hasParent && parent.isDebugIDContainerOnly() && t.isDebugAllowed(operationName) {
// debug模式,一定会采样
ctx.flags |= (flagSampled | flagDebug)
samplerTags = []Tag{{key: JaegerDebugHeader, value: parent.debugID}}
} else if sampled, tags := t.sampler.IsSampled(ctx.traceID, operationName); sampled {
//t.sampler是用户配置的采样策略
ctx.flags |= flagSampled
samplerTags = tags
}
} else {
ctx.traceID = parent.traceID
if rpcServer && t.options.zipkinSharedRPCSpan {
// Support Zipkin's one-span-per-RPC model
ctx.spanID = parent.spanID
ctx.parentID = parent.parentID
} else {
ctx.spanID = SpanID(t.randomID())
ctx.parentID = parent.spanID
}
// 如果存在parentTrace,直接使用parent的采样值
ctx.flags = parent.flags
}
if hasParent {
// copy baggage items
if l := len(parent.baggage); l > 0 {
ctx.baggage = make(map[string]string, len(parent.baggage))
for k, v := range parent.baggage {
ctx.baggage[k] = v
}
}
}
}
采样策略
概率采样策略是根据traceId判断是否需要采样。
func (s *ProbabilisticSampler) IsSampled(id TraceID, operation string) (bool, []Tag) {
return s.samplingBoundary >= id.Low, s.tags
}
span结束
span.go:FinishWithOptions
func (s *Span) FinishWithOptions(options opentracing.FinishOptions) {
if options.FinishTime.IsZero() {
options.FinishTime = s.tracer.timeNow()
}
s.observer.OnFinish(options)
s.Lock()
// 如果采样,才会计算耗时,日志等
if s.context.IsSampled() {
s.duration = options.FinishTime.Sub(s.startTime)
// Note: bulk logs are not subject to maxLogsPerSpan limit
if options.LogRecords != nil {
s.logs = append(s.logs, options.LogRecords...)
}
for _, ld := range options.BulkLogData {
s.logs = append(s.logs, ld.ToLogRecord())
}
}
s.Unlock()
// 上报到reporter
s.tracer.reportSpan(s)
}
func (t *Tracer) reportSpan(sp *Span) {
t.metrics.SpansFinished.Inc(1)
// 取span中的flag决定是否上报
if sp.context.IsSampled() {
t.reporter.Report(sp)
}
sp.Release()
}
网友评论