wrk против веб-клиента Vertx
Мы пытаемся протестировать приложение на основе java vertx, работающее с vertx 3.9.2 и openssl с поддержкой epoll. Мы наблюдаем огромную разницу между результатами wrk vs vertx webclient во время тестирования приложений. Wrk показывает, что мы можем поддерживать 50k rps, тогда как webclient может делать только 10k rps. Любая помощь в устранении проблемы будет очень полезна. Конечная точка healthz отвечает строкой json как {"success":"true"} с использованием метода end() без каких-либо определяемых пользователем заголовков.
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.core.net.OpenSSLEngineOptions;
import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.client.WebClientOptions;
import io.vertx.ext.web.client.predicate.ResponsePredicate;
import org.apache.commons.math3.stat.StatUtils;
import java.util.*;
import java.util.concurrent.*;
public class HttpTest {
public static void main(String[] args) throws InterruptedException {
int numberOfThreads = 20; //Integer.valueOf(args[0]);
int runDurationInMinutes = 2; //Integer.valueOf(args[1]);
final String trace_id = args.length == 3 ? args[2] : "http_test_" + UUID.randomUUID().toString();
System.out.println("Trace ID: " + trace_id);
ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads);
List<Integer> successfulRequestList = new Vector<Integer>();
List<Integer> failedRequestList = new Vector<Integer>();
List<Future<Integer>> futureList = new ArrayList<>();
Vertx vertx = Vertx.vertx(new VertxOptions().setPreferNativeTransport(true));
WebClient webClient = WebClient.create(vertx, new WebClientOptions()
.setUseAlpn(true)
.setSsl(true)
.setKeepAlive(true)
.setMaxPoolSize(numberOfThreads)
.setReuseAddress(true)
.setHttp2MaxPoolSize(numberOfThreads)
.setTcpKeepAlive(true)
.setKeepAliveTimeout(runDurationInMinutes * 60)
.setHttp2KeepAliveTimeout(runDurationInMinutes * 60)
.setPipelining(true)
.setTrustAll(true)
.setVerifyHost(false)
.setSslEngineOptions(new OpenSSLEngineOptions())
.setTcpQuickAck(true)
.setTcpNoDelay(true)
.setTcpFastOpen(true)
.setTcpCork(true)
.setIdleTimeout(runDurationInMinutes)
.setIdleTimeoutUnit(TimeUnit.MINUTES));
CountDownLatch healthRequest = new CountDownLatch(1);
// Just to start the vertx and avoid lag due to vertx startup
webClient
.get(443, "test-dev.test.com", "/healthz")
.ssl(true)
.send(handler -> {
if (handler.succeeded()) {
System.out.println("Health URL ping successful.");
} else {
System.err.println("Health URL ping failed.");
handler.cause().printStackTrace();
}
healthRequest.countDown();
});
healthRequest.await(60, TimeUnit.SECONDS);
final long startTime = System.currentTimeMillis();
long stopTime = System.currentTimeMillis() + (runDurationInMinutes * 60 * 1000);
Runnable runnable = () -> {
try {
while (!Thread.currentThread().isInterrupted() && System.currentTimeMillis() < stopTime) {
final long currentTime = System.currentTimeMillis();
try {
CountDownLatch webClientCountDown = new CountDownLatch(1);
//String url = urls.get(rand.nextInt(urls.size()));
webClient
.get(443, "test-dev.test.com", "/healthz")
.ssl(true)
.expect(ResponsePredicate.SC_OK)
.send(handler -> {
try {
if (handler.succeeded() && handler.result().body().toString().length() > 0) {
System.out.println(Long.toString(System.currentTimeMillis() - currentTime));
successfulRequestList.add(Integer.valueOf(Long.toString(System.currentTimeMillis() - currentTime)));
} else {
handler.cause().printStackTrace();
failedRequestList.add(Integer.valueOf(Long.toString(System.currentTimeMillis() - currentTime)));
}
} finally {
webClientCountDown.countDown();
}
});
webClientCountDown.await(30, TimeUnit.SECONDS);
} catch (Exception e) {
if (e instanceof InterruptedException || Thread.interrupted()) {
// Do nothing
} else {
failedRequestList.add(Integer.valueOf(Long.toString(System.currentTimeMillis() - currentTime)));
}
}
}
System.out.println(Calendar.getInstance() + "Completed ....");
} catch (Exception e) {
if (e instanceof InterruptedException) {
// Do nothing
} else {
e.printStackTrace();
}
}
};
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < numberOfThreads; i++) {
Thread thread = new Thread(runnable);
threads.add(thread);
Future testRequest = executor.submit(thread);
futureList.add(testRequest);
}
threads.forEach(thread -> {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("All users ramped up");
CountDownLatch countDownLatch = new CountDownLatch(1);
try {
System.out.println("Sleeping for " + runDurationInMinutes + " minute");
countDownLatch.await(runDurationInMinutes, TimeUnit.MINUTES);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Shutting down the executor");
for (Future<Integer> future : futureList) {
future.cancel(true);
}
executor.shutdown();
try {
if (!executor.awaitTermination(500, TimeUnit.MILLISECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
}
webClient.close();
vertx.close();
final long endTime = System.currentTimeMillis();
System.out.println("Sleeping for 5 seconds.....");
Thread.sleep(5 * 1000);
synchronized (successfulRequestList) {
final int successfulRequest = successfulRequestList.size();
System.out.println("Total Successful Requests: " + successfulRequest);
int failedRequest = 0;
synchronized (failedRequestList) {
failedRequest = failedRequestList.size();
System.out.println("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) {
System.out.println("Statistics | " + "99" + "%: " + StatUtils.percentile(doubleOfInt, 99));
System.out.println("Statistics | " + "99.1" + "%: " + StatUtils.percentile(doubleOfInt, 99.1));
System.out.println("Statistics | " + "99.2" + "%: " + StatUtils.percentile(doubleOfInt, 99.2));
System.out.println("Statistics | " + "99.3" + "%: " + StatUtils.percentile(doubleOfInt, 99.3));
System.out.println("Statistics | " + "99.4" + "%: " + StatUtils.percentile(doubleOfInt, 99.4));
System.out.println("Statistics | " + "99.5" + "%: " + StatUtils.percentile(doubleOfInt, 99.5));
System.out.println("Statistics | " + "99.6" + "%: " + StatUtils.percentile(doubleOfInt, 99.6));
System.out.println("Statistics | " + "99.7" + "%: " + StatUtils.percentile(doubleOfInt, 99.7));
System.out.println("Statistics | " + "99.8" + "%: " + StatUtils.percentile(doubleOfInt, 99.8));
System.out.println("Statistics | " + "99.9" + "%: " + StatUtils.percentile(doubleOfInt, 99.9));
System.out.println("Statistics | " + "99.99" + "%: " + StatUtils.percentile(doubleOfInt, 99.99));
System.out.println("Statistics | " + "99.999" + "%: " + StatUtils.percentile(doubleOfInt, 99.999));
}
System.out.println("Statistics | " + i + "%: " + StatUtils.percentile(doubleOfInt, i));
}
System.out.println("Throughput | " + (successfulRequest + failedRequest) / ((endTime - startTime) / 1000) + " tps");
doubleOfInt = null;
successfulRequestList.clear();
failedRequestList.clear();
}
}
}