import java.net.URI; import java.net.http.*; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.*; /** Closed-loop load: THREADS threads, WARMUP seconds discarded, then MEASURE seconds recorded. */ public class Load { public static void main(String[] a) throws Exception { int port = Integer.parseInt(a[0]), warmup = Integer.parseInt(a[1]), measure = Integer.parseInt(a[2]), threads = Integer.parseInt(a[3]); HttpClient c = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build(); HttpRequest rq = HttpRequest.newBuilder(URI.create("http://localhost:" + port + "/quote?n=200")).build(); long start = System.nanoTime(); long measureFrom = start + warmup * 1_000_000_000L, end = measureFrom + measure * 1_000_000_000L; List perThread = new CopyOnWriteArrayList<>(); ExecutorService ex = Executors.newFixedThreadPool(threads); List> fs = new ArrayList<>(); for (int t = 0; t < threads; t++) fs.add(ex.submit(() -> { long[] buf = new long[2_000_000]; int n = 0; try { while (true) { long s = System.nanoTime(); if (s >= end) break; c.send(rq, HttpResponse.BodyHandlers.discarding()); long e = System.nanoTime(); if (s >= measureFrom && n < buf.length) buf[n++] = e - s; } } catch (Exception e) { throw new RuntimeException(e); } perThread.add(Arrays.copyOf(buf, n)); return null; })); for (Future f : fs) f.get(); ex.shutdown(); long total = 0; for (long[] b : perThread) total += b.length; long[] all = new long[(int) total]; int p = 0; for (long[] b : perThread) { System.arraycopy(b, 0, all, p, b.length); p += b.length; } Arrays.sort(all); System.out.printf("requests=%d throughput=%.0f req/s p50=%.2f ms p99=%.2f ms p99.9=%.2f ms max=%.2f ms%n", total, total / (double) measure, all[(int) (total * 0.50)] / 1e6, all[(int) (total * 0.99)] / 1e6, all[(int) (total * 0.999)] / 1e6, all[(int) total - 1] / 1e6); } }