package com.ankurm.protocols; import com.ankurm.protocols.grpc.QuoteRequest; import com.ankurm.protocols.grpc.QuoteServiceGrpc; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.messaging.rsocket.RSocketRequester; import org.springframework.messaging.rsocket.RSocketStrategies; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; /** * Sequential request/response round trips, all three protocols, same JVM, same data source. * *
Read the numbers as a comparison of the three, not as absolutes. Client and server are the * same process on one loopback interface with two cores, so the network — the thing that * dominates every real deployment — is absent. That deletes the advantage a smaller * encoding would have on a real link and leaves framing and dispatch cost, which is exactly the * part a loopback measures well. */ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {"spring.rsocket.server.port=7001", "spring.grpc.server.port=9091"}) class RequestResponseBenchmarkTest { private static final int WARMUP = 2_000; private static final int MEASURED = 5_000; @LocalServerPort int httpPort; @Autowired RSocketStrategies strategies; @Test void threeProtocolsOneOperation() throws Exception { System.out.println("=== request/response, " + MEASURED + " sequential calls after " + WARMUP + " warm-up, loopback, JDK " + System.getProperty("java.version") + " ==="); System.out.println(grpc()); System.out.println(rsocket()); System.out.println(websocket()); } private String grpc() { ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 9091) .usePlaintext().build(); try { var stub = QuoteServiceGrpc.newBlockingStub(channel); QuoteRequest req = QuoteRequest.newBuilder().setSymbol("AAPL").build(); for (int i = 0; i < WARMUP; i++) { stub.getQuote(req); } Bench bench = new Bench(MEASURED); for (int i = 0; i < MEASURED; i++) { long t0 = System.nanoTime(); var quote = stub.getQuote(req); bench.record(System.nanoTime() - t0); assertThat(quote.getSymbol()).isEqualTo("AAPL"); } return bench.summary("gRPC"); } finally { channel.shutdownNow(); } } private String rsocket() { RSocketRequester requester = RSocketRequester.builder() .rsocketStrategies(strategies).tcp("localhost", 7001); try { for (int i = 0; i < WARMUP; i++) { requester.route("quote").data("AAPL").retrieveMono(Quote.class).block(); } Bench bench = new Bench(MEASURED); for (int i = 0; i < MEASURED; i++) { long t0 = System.nanoTime(); Quote quote = requester.route("quote").data("AAPL") .retrieveMono(Quote.class).block(); bench.record(System.nanoTime() - t0); assertThat(quote).isNotNull(); } return bench.summary("RSocket"); } finally { requester.dispose(); } } private String websocket() throws Exception { try (WsClient client = new WsClient(httpPort, 64)) { for (int i = 0; i < WARMUP; i++) { client.send("QUOTE AAPL"); assertThat(client.take(5_000)).isNotNull(); } Bench bench = new Bench(MEASURED); for (int i = 0; i < MEASURED; i++) { long t0 = System.nanoTime(); client.send("QUOTE AAPL"); String reply = client.take(5_000); bench.record(System.nanoTime() - t0); assertThat(reply).contains("AAPL"); } return bench.summary("WebSocket"); } } }