SpringBoot2 Blocking Web vs Reactive Web
Tags: java, springboot
Published: 2018-03-25 17:39:54

---
title: SpringBoot2 Blocking Web vs Reactive Web
published: true
description: Trying Spring Reactive Web style and check the performance
tags: #java #springboot
---


Hello, I'm Mitz. This is the first post on dev.to. 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.

![Reactive](https://thepracticaldev.s3.amazonaws.com/i/rwxl8gcedmhv5o3lbte0.png)

# 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](https://thepracticaldev.s3.amazonaws.com/i/sh7w6d8vc5cj05yhjw2h.png)
(Image from https://spring.io/ )

# Demo Apps Architecture

I found a great article which compares SpringBoot1 and SpringBoot2:
[Raw Performance Numbers - Spring Boot 2 Webflux vs. Spring Boot 1](https://dzone.com/articles/raw-performance-numbers-spring-boot-2-webflux-vs-s).
I did almost the same thing to compare SpringBoot2 Blocking Web vs Reactive Web. The architecture is like this:

![Architecture](https://thepracticaldev.s3.amazonaws.com/i/4uhi2r4b5wvdkbx0byp9.png)

There're 3 apps:

* delay-service
* blocking-app
* reactive-app

The source code is here:
https://github.com/bufferings/webflux-demo-201803

## delay-service

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

```java
  @GetMapping("/{delayMillis}")
  public Mono<String> get(@PathVariable int delayMillis) {
    return Mono.just("OK")
        .delayElement(Duration.ofMillis(delayMillis));
  }
```

## blocking-app

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

```java
  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();
  }

  @GetMapping("/{delayMillis}")
  public String get(@PathVariable String delayMillis) {
    String result = client.getForObject("/" + delayMillis, String.class);
    return "Blocking:" + result;
  }
```

## reactive-app

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

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

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

  @GetMapping("/{delayMillis}")
  public Mono<String> get(@PathVariable String delayMillis) {
    return client.get()
        .uri("/" + delayMillis)
        .retrieve()
        .bodyToMono(String.class)
        .map(s -> "Reactive:" + s);
  }
```

# Let's check the performance!

Start delay-service:

```bash
./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
```

Start blocking-app:

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

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

Start reactive-app:

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

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

Now, all three apps are running.

## Load Test Scenario

I used Gatling(https://gatling.io/) 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.

```scala
  val myScenario = scenario("Webflux Demo").exec(
    repeat(30) {
      exec(
        http("request_1").get(targetUrl)
      ).pause(1 second, 2 seconds)
    }
  )
  setUp(myScenario.inject(rampUsers(simUsers).over(30 seconds)))
```

## Gatling to blocking-app

```bash
./gradlew -p apps/load-test -DTARGET_URL=http://localhost:8081/300 \
    -DSIM_USERS=1000 gatlingRun
```

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](https://thepracticaldev.s3.amazonaws.com/i/xit5gmoeavba9z7xg2wm.png)

## Gatling to reactive-app

```bash
./gradlew -p apps/load-test -DTARGET_URL=http://localhost:8082/300 \
    -DSIM_USERS=1000 gatlingRun
```

It only use 4 threads to handle the request.
![reactive-app threads](https://thepracticaldev.s3.amazonaws.com/i/frrteunq1w2a4gxq3ctb.png)

# 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!

## blocking-app

with 1000 users:
![blocking1000](https://thepracticaldev.s3.amazonaws.com/i/a8j4tphcuyub9hjtt8qo.png)

with 3000 users:
![blocking3000](https://thepracticaldev.s3.amazonaws.com/i/70qqxhqy4s2jfqofsiyf.png)

with 6000 users:
![blocking6000](https://thepracticaldev.s3.amazonaws.com/i/1tbmokeg2uni81kja5si.png)

## reactive-app

with 1000 users:
![reactive1000](https://thepracticaldev.s3.amazonaws.com/i/4mu6puyip19yt5gfufo4.png)

with 3000 users:
![reactive3000](https://thepracticaldev.s3.amazonaws.com/i/6twi83fhanii7lxzwh9m.png)

with 6000 users:
![reactive6000](https://thepracticaldev.s3.amazonaws.com/i/4wnzhlvc2gqwufm83sab.png)

# 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.

# Conclusion

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!