DEV Community


Posted on • Updated on

SpringBoot2 Blocking Web vs Reactive Web

Hello, I'm Mitz. This is the first post on Nice to meet you :)

Buzzwords into my ToolBox

As many of you've heard Microservices, Blockchain, etc, there're a variety of tech buzzwords and they come and go often. Therefore, it's important for us not to believe them without even checking, but to see for ourselves how they work then put them into our toolbox. That's why I tried using a part of Reactive Programming this time.

One of the benefit of Reactive Programming is that we can use machine resources effectively. For example, in case of a web application, a server can handle more requests than blocking style application with less threads.


SpringBoot2 Reactive Web

SpringBoot2, which was released at the beginning of this month, has introduced "Reactive Web" feature. So I tried comparing "Spring Web" and "Spring Reactive Web". "Spring Web" is based on a conventional blocking style with Servlet, and "Spring Reactive Web" is a new style with reactive programming.

Spring Web Reactive
(Image from )

Demo Apps Architecture

I found a great article which compares SpringBoot1 and SpringBoot2:
Raw Performance Numbers - Spring Boot 2 Webflux vs. Spring Boot 1.
I did almost the same thing to compare SpringBoot2 Blocking Web vs Reactive Web. The architecture is like this:


There're 3 apps:

  • delay-service
  • blocking-app
  • reactive-app

The source code is here:


The delay-service emulates an outside API with some latency. We can set the latency with a path parameter:

  public Mono<String> get(@PathVariable int delayMillis) {
    return Mono.just("OK")
Enter fullscreen mode Exit fullscreen mode


The blocking-app is a simple Spring Web app, which calls delay-service in a blocking manner and returns it with a blocking manner:

  private static final String DELAY_SERVICE_URL = "http://localhost:8080";

  private final RestTemplate client;

  public BlockingApp(RestTemplateBuilder builder) {
    client = builder.rootUri(DELAY_SERVICE_URL).build();

  public String get(@PathVariable String delayMillis) {
    String result = client.getForObject("/" + delayMillis, String.class);
    return "Blocking:" + result;
Enter fullscreen mode Exit fullscreen mode


The reactive-app is a Spring Web Reactive app, which calls delay-service with a reactive client and returns Mono:

  private static final String DELAY_SERVICE_URL = "http://localhost:8080";

  private final WebClient client = WebClient.create(DELAY_SERVICE_URL);

  public Mono<String> get(@PathVariable String delayMillis) {
    return client.get()
        .uri("/" + delayMillis)
        .map(s -> "Reactive:" + s);
Enter fullscreen mode Exit fullscreen mode

Let's check the performance!

Start delay-service:

./gradlew -p apps/delay-service clean bootRun

curl -w "\n%{time_total}s\n" localhost:8080/1000
# returns "OK" after 1000ms

curl -w "\n%{time_total}s\n" localhost:8080/2000
# returns "OK" after 2000ms
Enter fullscreen mode Exit fullscreen mode

Start blocking-app:

./gradlew -p apps/blocking-app clean bootRun

curl -w "\n%{time_total}s\n" localhost:8081/2000
# returns "Blocking:OK" after 2000ms
Enter fullscreen mode Exit fullscreen mode

Start reactive-app:

./gradlew -p apps/reactive-app clean bootRun

curl -w "\n%{time_total}s\n" localhost:8082/2000
# returns "Reactive:OK" after 2000ms
Enter fullscreen mode Exit fullscreen mode

Now, all three apps are running.

Load Test Scenario

I used Gatling( for the load test.

The scenario is something like this "1000 users call the API 30 times with 1 to 2 sec intervals". I would like to set the latency of delay-service as 300ms.

  val myScenario = scenario("Webflux Demo").exec(
    repeat(30) {
      ).pause(1 second, 2 seconds)
  setUp(myScenario.inject(rampUsers(simUsers).over(30 seconds)))
Enter fullscreen mode Exit fullscreen mode

Gatling to blocking-app

./gradlew -p apps/load-test -DTARGET_URL=http://localhost:8081/300 \
    -DSIM_USERS=1000 gatlingRun
Enter fullscreen mode Exit fullscreen mode

With VisualVM, we can see the worker threads count increase up to 200 which is the default maxThread value of the Tomcat.
blocking-app threads

Gatling to reactive-app

./gradlew -p apps/load-test -DTARGET_URL=http://localhost:8082/300 \
    -DSIM_USERS=1000 gatlingRun
Enter fullscreen mode Exit fullscreen mode

It only use 4 threads to handle the request.
reactive-app threads

Load Test Result

Here's the result.

As you can see, for 1000 users both apps work nicely with around 300ms response time as we expected. But for 3000 & 6000 users, the 95 percentile of blocking-app becomes worse.

On the other hand, reactive-app keeps the good response speed around 400ms and it shows about 2000rps with my laptop(Core i7-7500U 2.7GHz/16GB RAM).

Interesting result!


with 1000 users:

with 3000 users:

with 6000 users:


with 1000 users:

with 3000 users:

with 6000 users:

What's left

Increasing Tomcat maxThreads

Since I decided to try with the default config, the Tomcat threads count reached to 200 which is the default value of maxThreads. Probably tuning maxThread would improve the blocking-app performance.

Separating the env

Since I tried this demo all in my laptop, all the apps affected each other regarding the resource usage. So separating each apps into several machine might show different result.


In conclusion, we could know how SpringBoot2 Reactive Web handles requests efficiently. But I recommend you to check it by yourself. Especially Reactive Web style programming requires Java engineer to change their mindset to some extent I think.

Anyway, it's so interesting and I'm feeling now SpringBoot2 Reactive Web is in my toolbox. Thank you!

Top comments (0)