Merge pull request #1103 from DataDog/landerson/load-generator

Simple load generator tool
This commit is contained in:
Laplie Anderson 2019-11-19 10:19:14 -05:00 committed by GitHub
commit 99acb52589
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 166 additions and 0 deletions

View File

@ -9,3 +9,4 @@ logback.xml,net.logstash.logback,Apache-2.0,
import (test),org.junit,EPL-1.0,Copyright © 2002-2017 JUnit. All Rights Reserved.
import (test),org.assertj,Apache-2.0,Copyright 2012-2017 the original author or authors.
import (test),org.mockito,MIT,Copyright (c) 2007 Mockito contributors
import (test),info.picocli,Apache-2.0,

1 Component Origin License Copyright
9 import (test) org.junit EPL-1.0 Copyright © 2002-2017 JUnit. All Rights Reserved.
10 import (test) org.assertj Apache-2.0 Copyright 2012-2017 the original author or authors.
11 import (test) org.mockito MIT Copyright (c) 2007 Mockito contributors
12 import (test) info.picocli Apache-2.0

View File

@ -0,0 +1,22 @@
Load Generator
=====
Generates a simulated trace load. Run with
```
./gradlew launch --args='--rate 10'
```
**OR**
```
./gradlew :dd-java-agent:load-generator:launch --args='--rate 10'
```
from the root of the repo.
To print all options, use:
```
--args='--help'
```

View File

@ -0,0 +1,18 @@
apply from: "${rootDir}/gradle/java.gradle"
dependencies {
compile project(':dd-trace-api')
compile project(':dd-trace-ot')
compile 'info.picocli:picocli:4.0.4'
compile deps.guava
}
task launch(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
main = 'datadog.loadgenerator.LoadGenerator'
jvmArgs = ["-javaagent:${project(':dd-java-agent').shadowJar.archivePath}", "-Ddd.service.name=loadtest"]
systemProperties System.properties
dependsOn project(':dd-java-agent').shadowJar
}

View File

@ -0,0 +1,124 @@
package datadog.loadgenerator;
import com.google.common.util.concurrent.RateLimiter;
import io.opentracing.Scope;
import io.opentracing.Span;
import io.opentracing.Tracer;
import io.opentracing.util.GlobalTracer;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
@Command(
mixinStandardHelpOptions = true,
description = "Generates traces and spans at a specified rate")
public class LoadGenerator implements Callable<Integer> {
@Option(names = "--rate", required = true, description = "rate, per second, to generate traces")
private int rate;
@Option(
names = "--threads",
defaultValue = "6",
description = "Number of trace-generating threads (default: ${DEFAULT-VALUE})")
private int threads;
@Option(
names = "--width",
defaultValue = "2",
description = "Number of spans directly below the root (default: ${DEFAULT-VALUE})")
private int width;
@Option(
names = "--depth",
defaultValue = "3",
description = "Total spans deep per trace, including parent (default: ${DEFAULT-VALUE})")
private int depth;
@Option(
names = "--warmup",
defaultValue = "5",
description = "Time, in seconds, to ramp up to target rate (default: ${DEFAULT-VALUE})")
private int warmupPeriod;
@Option(
names = "--print-interval",
defaultValue = "20",
description = "Interval, in seconds, to print statistics (default: ${DEFAULT-VALUE})")
private int printInterval;
private RateLimiter rateLimiter;
private final AtomicLong tracesSent = new AtomicLong();
@Override
public Integer call() throws Exception {
rateLimiter = RateLimiter.create(rate, warmupPeriod, TimeUnit.SECONDS);
long intervalStart = System.currentTimeMillis();
long tracesAtLastReport = 0;
for (int i = 0; i < threads; i++) {
final Thread workerThread = new Thread(new Worker(), "Worker-" + i);
workerThread.setDaemon(true);
workerThread.start();
}
while (true) {
Thread.sleep(printInterval * 1000);
final long currentTracesSent = tracesSent.get();
final long intervalEnd = System.currentTimeMillis();
final double currentRate =
(currentTracesSent - tracesAtLastReport) / ((intervalEnd - intervalStart) / 1000d);
System.out.println(
"Total Traces Sent: " + currentTracesSent + ", Rate this interval: " + currentRate);
intervalStart = System.currentTimeMillis();
tracesAtLastReport = currentTracesSent;
}
}
public static void main(final String[] args) {
final int exitCode = new CommandLine(new LoadGenerator()).execute(args);
System.exit(exitCode);
}
private class Worker implements Runnable {
@Override
public void run() {
final Tracer tracer = GlobalTracer.get();
while (true) {
rateLimiter.acquire();
final Span parent = tracer.buildSpan("parentSpan").start();
try (final Scope scope = tracer.activateSpan(parent)) {
for (int i = 0; i < width; i++) {
final Span widthSpan = tracer.buildSpan("span-" + i).start();
try (final Scope widthScope = tracer.activateSpan(widthSpan)) {
for (int j = 0; j < depth - 2; j++) {
final Span depthSpan = tracer.buildSpan("span-" + i + "-" + j).start();
try (final Scope depthScope = tracer.activateSpan(depthSpan)) {
// do nothing. Maybe sleep? but that will mean we need more threads to keep the
// effective rate
} finally {
depthSpan.finish();
}
}
} finally {
widthSpan.finish();
}
}
} finally {
parent.finish();
}
tracesSent.getAndIncrement();
}
}
}
}

View File

@ -27,6 +27,7 @@ include ':dd-java-agent'
include ':dd-java-agent:agent-bootstrap'
include ':dd-java-agent:agent-tooling'
include ':dd-java-agent:agent-jmxfetch'
include ':dd-java-agent:load-generator'
// misc
include ':dd-java-agent:testing'