Проблема теста производительности веб-клиента Vertx
Я пытаюсь написать тест производительности, чтобы проверить производительность моего API. Я использую веб-клиент vertx для получения тестов. Веб-клиент не вызывается для начальных запросов, а для всех других запросов он также показывает гораздо больше времени, чем на сервере. Сервер, на который я обращаюсь, работает на другом компьютере и не работает на локальном хосте. Я использую vertx 3.9.2. Посмотрите на фрагмент кода ниже и предположите, что я делаю неправильно.
@Slf4j
public class HttpTest {
public static void main(String[] args) throws InterruptedException {
System.setProperty("vertx.logger-delegate-factory-class-name", "io.vertx.core.logging.SLF4JLogDelegateFactory");
InternalLoggerFactory.setDefaultFactory(Slf4JLoggerFactory.INSTANCE);
Vertx vertx = Vertx.vertx(new VertxOptions()
.setPreferNativeTransport(true)
.setEventLoopPoolSize(2 * CpuCoreSensor.availableProcessors())
.setWarningExceptionTime(TimeUnit.SECONDS.toNanos(2)));
...
for (int i = 0; i < numberOfThreads; i++) { // numberOfThreads = 20, runDurationInMinutes=2
Future<Integer> testRequest = ((ExecutorService) executor).submit(new TestRequests(successfulRequestList, failedRequestList, url, System.currentTimeMillis() + (runDurationInMinutes * 60 * 1000), vertx));
futureList.add(testRequest);
Thread.sleep(1 * 100);
}
...
}
public class TestRequests implements Callable<Integer> {
private final List<Integer> successfulRequestList;
private final List<Integer> failedRequestList;
private final String url;
private WebClient webClient;
private final long stopTime;
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
private boolean flag = true;
public TestRequests(List<Integer> successfulRequestList, List<Integer> failedRequestList, String url, long stopTime, Vertx vertx) {
this.successfulRequestList = successfulRequestList;
this.failedRequestList = failedRequestList;
this.url = url;
this.stopTime = stopTime;
WebClientOptions webClientOptions = new WebClientOptions().setUseAlpn(true).setSslEngineOptions(new OpenSSLEngineOptions()).setKeepAlive(true).setSsl(true).setLogActivity(false);
webClient = WebClient.create(vertx, webClientOptions);
this.webClient = webClient;
}
@Override
public Integer call() throws Exception {
final String requestBody = "... Some String ...";
try {
while (!Thread.currentThread().isInterrupted() && System.currentTimeMillis() < stopTime) {
long currentTime = System.currentTimeMillis();
try {
log.info("Sending request");
webClient
.postAbs("https://paceoffers-dev.test.com/graphql")
.ssl(true)
.putHeader("Trace_Id", "gt1")
.putHeader("Span_Id", UUID.randomUUID().toString())
.putHeader("Authorization", UUID.randomUUID().toString())
.putHeader("X-TEST-API-KEY", UUID.randomUUID().toString())
.putHeader("validate_hmac", "false")
.putHeader("Content-Type", "application/json")
.rxSendBuffer(Buffer.buffer(requestBody))
.subscribe(
result -> {
if (result != null && result.statusCode() == 200) {
log.info("x-response-time: {} timeTaken: {}ms", result.getHeader("x-response-time"), System.currentTimeMillis() - currentTime);
successfulRequestList.add(Integer.valueOf(Long.toString(System.currentTimeMillis() - currentTime)));
} else {
log.error("statusCode: {} timeTaken: {}ms", result.statusCode(), System.currentTimeMillis() - currentTime);
failedRequestList.add(Integer.valueOf(Long.toString(System.currentTimeMillis() - currentTime)));
}
},
error -> {
log.error("Uncaught exception", error);
}
).dispose();
//log.info("Completed sending request");
} catch (Exception e) {
if (e instanceof InterruptedException || Thread.interrupted()) {
return null;
}
e.printStackTrace();
failedRequestList.add(Integer.valueOf(Long.toString(System.currentTimeMillis() - currentTime)));
}
}
} catch (
Exception e) {
if (e instanceof InterruptedException) {
return null;
}
}
return null;
}
---- ОБНОВИТЬ ----
Я изменил код, чтобы его можно было запускать как отдельное приложение. Я использую рабочий поток vertx. Проблема все еще существует.
Это автономный исполняемый код.
import io.netty.util.internal.logging.InternalLoggerFactory;
import io.netty.util.internal.logging.Slf4JLoggerFactory;
import io.vertx.core.VertxOptions;
import io.vertx.core.impl.cpu.CpuCoreSensor;
import io.vertx.core.net.OpenSSLEngineOptions;
import io.vertx.ext.web.client.WebClientOptions;
import io.vertx.reactivex.core.Vertx;
import io.vertx.reactivex.core.WorkerExecutor;
import io.vertx.reactivex.core.buffer.Buffer;
import io.vertx.reactivex.ext.web.client.HttpResponse;
import io.vertx.reactivex.ext.web.client.WebClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.math3.stat.StatUtils;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@Slf4j
public class HttpTest {
public static void main(String[] args) throws InterruptedException {
System.setProperty("vertx.logger-delegate-factory-class-name", "io.vertx.core.logging.SLF4JLogDelegateFactory");
System.setProperty("vertx.disableDnsResolver", Boolean.TRUE.toString());
InternalLoggerFactory.setDefaultFactory(Slf4JLoggerFactory.INSTANCE);
Vertx vertx = Vertx.vertx(new VertxOptions()
.setPreferNativeTransport(true)
.setEventLoopPoolSize(2 * CpuCoreSensor.availableProcessors())
.setWarningExceptionTime(TimeUnit.SECONDS.toNanos(2)));
log.info("vertx.isNativeTransportEnabled(): " + vertx.isNativeTransportEnabled());
if (args.length < 3) {
log.info("Argument 1(Number of Threads) - Integer, Argument 2(Test run duration in minutes) - Integer, Argument 3(The HTTPS POST url): String");
System.exit(1);
}
int numberOfThreads = Integer.valueOf(args[0]);
int runDurationInMinutes = Integer.valueOf(args[1]);
String url = args[2];
List<Integer> successfulRequestList = new Vector<Integer>();
List<Integer> failedRequestList = new Vector<Integer>();
CountDownLatch countDownLatch = new CountDownLatch(1);
WorkerExecutor sharedWorkerExecutor = vertx.createSharedWorkerExecutor("vertx-worker", numberOfThreads, runDurationInMinutes * 60 + 5, TimeUnit.SECONDS);
WebClientOptions webClientOptions = new WebClientOptions().setUseAlpn(true).setSslEngineOptions(new OpenSSLEngineOptions()).setKeepAlive(true).setIdleTimeout(60).setIdleTimeoutUnit(TimeUnit.SECONDS).setSsl(true).setTcpKeepAlive(true).setLogActivity(false);
if (vertx.isNativeTransportEnabled()) {
webClientOptions.setTcpCork(true).setTcpNoDelay(true).setTcpQuickAck(true);
}
WebClient webClient = WebClient.create(vertx, webClientOptions);
final long startTime = System.currentTimeMillis();
for (int i = 0; i < numberOfThreads; i++) {
sharedWorkerExecutor.executeBlocking(f -> {
log.info("Starting");
final long stopTime = System.currentTimeMillis() + (runDurationInMinutes * 60 * 1000);
final String requestBody = "{ \"query\": \"query($effts: Instant!, $planId: String!){ getOfferDetailsByPlanIdentifierAndEffectiveDate(effective_timestamp: $effts, plan_identifier: $planId) { name legal_names { value language } effective_timestamp end_timestamp jurisdiction { category name } category sub_category is_arrangement type features { identifier name configurations { identifier name duration { unit value } values { jurisdiction { category name } values } } terms { identifier name duration { unit value } values } } } } \", \"variables\": {\"effts\": \"2021-06-22T08:00:00.00Z\", \"planId\": \"P68\"} }";
while (!Thread.currentThread().isInterrupted() && System.currentTimeMillis() < stopTime) {
final long currentTime = System.currentTimeMillis();
try {
HttpResponse<io.vertx.reactivex.core.buffer.Buffer> result = webClient
.postAbs(url)
.ssl(true)
.putHeader("Trace_Id", "gt1")
.putHeader("Span_Id", UUID.randomUUID().toString())
.putHeader("Authorization", UUID.randomUUID().toString())
.putHeader("X-TEST-API-KEY", UUID.randomUUID().toString())
.putHeader("validate_hmac", "false")
.putHeader("Content-Type", "application/json")
.rxSendBuffer(Buffer.buffer(requestBody))
.map(resultVar -> {
log.info("Time taken: " + (System.currentTimeMillis() - currentTime));
return resultVar;
})
.blockingGet();
if (result != null && result.statusCode() == 200) {
log.info("x-response-time: {} timeTaken: {}ms", result.getHeader("x-response-time"), System.currentTimeMillis() - currentTime);
successfulRequestList.add(Integer.valueOf(Long.toString(System.currentTimeMillis() - currentTime)));
} else {
log.error("statusCode: {} timeTaken: {}ms", result.statusCode(), System.currentTimeMillis() - currentTime);
failedRequestList.add(Integer.valueOf(Long.toString(System.currentTimeMillis() - currentTime)));
}
} catch (Exception e) {
if (e instanceof InterruptedException || (e instanceof RuntimeException && e.getCause() != null && e.getCause() instanceof InterruptedException) ||Thread.interrupted()) {
// Do nothing
} else {
log.error("Uncaught Exception", e);
failedRequestList.add(Integer.valueOf(Long.toString(System.currentTimeMillis() - currentTime)));
}
}
}
f.complete(Thread.currentThread().getName());
}, handler -> {
log.info("Completed processing for: {}", handler.result().toString());
});
}
log.info("All users ramped up");
try {
log.info("Sleeping for " + runDurationInMinutes + " minute");
countDownLatch.await(runDurationInMinutes, TimeUnit.MINUTES);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("Shutting down the executor");
sharedWorkerExecutor.close();
vertx.close();
final long endTime = System.currentTimeMillis();
log.info("Sleeping for 5 seconds.....");
Thread.sleep(5 * 1000);
synchronized (successfulRequestList) {
final int successfulRequest = successfulRequestList.size();
log.info("Total Successful Requests: " + successfulRequest);
int failedRequest = 0;
synchronized (failedRequestList) {
failedRequest = failedRequestList.size();
log.info("Total Failed Requests: " + failedRequest);
}
double[] doubleOfInt = successfulRequestList.parallelStream().mapToDouble(i -> i).toArray();
Arrays.sort(doubleOfInt);
for (int i = 5; i <= 100; i = i + 5) {
if (i == 100) {
log.info("Statistics | " + "99" + "%: " + StatUtils.percentile(doubleOfInt, 99));
log.info("Statistics | " + "99.1" + "%: " + StatUtils.percentile(doubleOfInt, 99.1));
log.info("Statistics | " + "99.2" + "%: " + StatUtils.percentile(doubleOfInt, 99.2));
log.info("Statistics | " + "99.3" + "%: " + StatUtils.percentile(doubleOfInt, 99.3));
log.info("Statistics | " + "99.4" + "%: " + StatUtils.percentile(doubleOfInt, 99.4));
log.info("Statistics | " + "99.5" + "%: " + StatUtils.percentile(doubleOfInt, 99.5));
log.info("Statistics | " + "99.6" + "%: " + StatUtils.percentile(doubleOfInt, 99.6));
log.info("Statistics | " + "99.7" + "%: " + StatUtils.percentile(doubleOfInt, 99.7));
log.info("Statistics | " + "99.8" + "%: " + StatUtils.percentile(doubleOfInt, 99.8));
log.info("Statistics | " + "99.9" + "%: " + StatUtils.percentile(doubleOfInt, 99.9));
log.info("Statistics | " + "99.99" + "%: " + StatUtils.percentile(doubleOfInt, 99.99));
log.info("Statistics | " + "99.999" + "%: " + StatUtils.percentile(doubleOfInt, 99.999));
}
log.info("Statistics | " + i + "%: " + StatUtils.percentile(doubleOfInt, i));
}
log.info("Throughput | " + (successfulRequest + failedRequest) / ((endTime - startTime) / 1000) + " tps");
doubleOfInt = null;
successfulRequestList.clear();
failedRequestList.clear();
}
}
}