diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/TracerContext.java b/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/TracerContext.java index e1cc0d650..b49319455 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/TracerContext.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/TracerContext.java @@ -2,12 +2,14 @@ package org.skywalking.apm.agent.core.context; import java.util.LinkedList; import java.util.List; +import org.skywalking.apm.agent.core.boot.ServiceManager; import org.skywalking.apm.agent.core.conf.Config; import org.skywalking.apm.agent.core.context.trace.AbstractSpan; import org.skywalking.apm.agent.core.context.trace.LeafSpan; import org.skywalking.apm.agent.core.context.trace.Span; import org.skywalking.apm.agent.core.context.trace.TraceSegment; import org.skywalking.apm.agent.core.context.trace.TraceSegmentRef; +import org.skywalking.apm.agent.core.sampling.SamplingService; /** * {@link TracerContext} maintains the context. @@ -73,17 +75,42 @@ public final class TracerContext implements AbstractTracerContext { span = new Span(spanIdGenerator++, operationName, startTime); } push(span); - } else if (parentSpan.isLeaf()) { - span = parentSpan; - LeafSpan leafSpan = (LeafSpan)span; - leafSpan.push(); } else { - if (isLeaf) { - span = new LeafSpan(spanIdGenerator++, parentSpan, operationName, startTime); - } else { - span = new Span(spanIdGenerator++, parentSpan, operationName, startTime); + /** + * Don't have ref yet, means this isn't part of distributed trace. + * Use sampling mechanism + * Only check this on the second span, + * because the {@link #extract(ContextCarrier)} invoke before create the second span. + */ + if (spanIdGenerator == 1) { + SamplingService samplingService = ServiceManager.INSTANCE.findService(SamplingService.class); + if (segment.hasRef()) { + samplingService.forceSampled(); + } else { + if (!samplingService.trySampling()) { + /** + * Don't sample this trace. + * Now, switch this trace as an {@link IgnoredTracerContext}, + * further more, we will provide an analytic tracer context for all metrics in this trace. + */ + ContextManager.ContextSwitcher.INSTANCE.toNew(new IgnoredTracerContext(2)); + return ContextManager.activeSpan(); + } + } + } + + if (parentSpan.isLeaf()) { + span = parentSpan; + LeafSpan leafSpan = (LeafSpan)span; + leafSpan.push(); + } else { + if (isLeaf) { + span = new LeafSpan(spanIdGenerator++, parentSpan, operationName, startTime); + } else { + span = new Span(spanIdGenerator++, parentSpan, operationName, startTime); + } + push(span); } - push(span); } return span; } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/trace/TraceSegment.java b/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/trace/TraceSegment.java index d39ecb354..eeb4c3a11 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/trace/TraceSegment.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/trace/TraceSegment.java @@ -178,6 +178,10 @@ public class TraceSegment { return Collections.unmodifiableList(refs); } + public boolean hasRef() { + return !(refs == null || refs.size() == 0); + } + public List getRelatedGlobalTraces() { return relatedGlobalTraces.getRelatedGlobalTraces(); } diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/sampling/SamplingService.java b/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/sampling/SamplingService.java index a55136a3d..6dd43c52a 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/sampling/SamplingService.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/sampling/SamplingService.java @@ -1,8 +1,11 @@ package org.skywalking.apm.agent.core.sampling; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import org.skywalking.apm.agent.core.boot.BootService; import org.skywalking.apm.agent.core.conf.Config; -import org.skywalking.apm.agent.core.context.ContextCarrier; import org.skywalking.apm.agent.core.context.trace.TraceSegment; import org.skywalking.apm.logging.ILog; import org.skywalking.apm.logging.LogManager; @@ -20,29 +23,53 @@ public class SamplingService implements BootService { private static final ILog logger = LogManager.getLogger(SamplingService.class); private volatile boolean on = false; - private volatile int rollingSeed = 1; + private volatile AtomicInteger samplingFactorHolder; @Override public void bootUp() throws Throwable { if (Config.Agent.SAMPLE_N_PER_10_SECS > 0) { on = true; + this.resetSamplingFactor(); + ScheduledExecutorService service = Executors + .newSingleThreadScheduledExecutor(); + service.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + resetSamplingFactor(); + } + }, 1, 1, TimeUnit.SECONDS); + logger.debug("Agent sampling mechanism started. Sample {} traces in 10 seconds.", Config.Agent.SAMPLE_N_PER_10_SECS); } } - public void trySampling(TraceSegment segment) { + /** + * @return true, if sampling mechanism is on, and get the sampling factor successfully. + */ + public boolean trySampling() { + if (on) { + int factor = samplingFactorHolder.get(); + if (factor < Config.Agent.SAMPLE_N_PER_10_SECS) { + return samplingFactorHolder.compareAndSet(factor, factor + 1); + } else { + return false; + } + } + return true; } /** - * Set the {@link TraceSegment} to sampled, when {@link ContextCarrier} contains "isSampled" flag. - *

- * A -> B, if TraceSegment is sampled in A, then the related TraceSegment in B must be sampled, no matter you - * sampling rate. And reset the {@link #rollingSeed}, in case of too many {@link TraceSegment}s, which started in - * this JVM, are sampled. - * - * @param segment the current TraceSegment. - * @param carrier + * Increase the sampling factor by force, + * to avoid sampling too many traces. + * If many distributed traces require sampled, + * the trace beginning at local, has less chance to be sampled. */ - public void setSampleWhenExtract(TraceSegment segment, ContextCarrier carrier) { + public void forceSampled() { + if (on) { + samplingFactorHolder.incrementAndGet(); + } } + private void resetSamplingFactor() { + samplingFactorHolder = new AtomicInteger(0); + } }