Spring 5 is currently on v5.0.0.RC3. (Release Candidate 3), released on July 2017, the goal is, GA right after JDK 9’s GA date(Available from the 21.9.2017). The new version of spring is written in Java 8, but will be fully compatible with the next version, Java 9. If you want to know everything about the new Spring 5, check What’s New in the Spring Framework and Spring 5 Documentation. Before starting to talk about Spring 5, let’s talk first about Reactive Systems and Programming .

Reactive Programming

If you never heard about reactive systems, I recommend you to read The Reactive Manifesto. In summary a reactive system must be Responsive, Resilient, Elastic and Message Driven.

Large systems are composed of smaller ones and therefore depend on the Reactive properties of their constituents. This means that Reactive Systems apply design principles so these properties apply at all levels of scale, making them composable.

Reactive programming is non-blocking applications that are asynchronous and event-driven and require a small number of threads to scale vertically (i.e. within the JVM) rather than horizontally (i.e. through clustering).

Reactive programming can be used in different use cases , let's see some examples:

External Service Calls: Many backend services these days are REST-ful, HTTP is fundamentally blocking and synchronous. IO operations are usually blocking operations, we don't want to wait for one call to complete before sending the next request.

Highly Concurrent Message Consumers: Reactive patterns are by nature a good fit for message processing.

Abstraction Over (A)synchronous Processing: Functional Reactive Programming it's handy providing some extra layer of abstraction, since it's not necessary to know if code that we are calling is synchronous or asynchronous.

Spring Web Reactive Module

Spring Framework 5 includes a new spring-web-reactive module. The module contains support for reactive HTTP and WebSocket clients as well as for reactive server web applications including REST, HTML browser, and WebSocket style interactions.

Let’s do it

First, we need to create a Spring Boot Application using Spring Initializr. If you don’t have enough experience with Spring Boot please read this. The base code can be found at the end of the article.

Adding Dependencies

Got to your pom.xml at the root folder and add the dependencies for Spring Web Flux , MondoDB, Gatling and Test.

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-webflux</artifactId>

</dependency>



<!--EMBED MONGO DB-->



<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-commons -->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>

</dependency>



<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-mongodb</artifactId>

</dependency>



<dependency>

<groupId>de.flapdoodle.embed</groupId>

<artifactId>de.flapdoodle.embed.mongo</artifactId>

<version>1.50.5</version>

</dependency>



<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

</dependency>



<!--GATLING-->

<!-- https://mvnrepository.com/artifact/io.gatling.highcharts/gatling-charts-highcharts -->

<dependency>

<groupId>io.gatling.highcharts</groupId>

<artifactId>gatling-charts-highcharts</artifactId>

<version>2.2.5</version>

<scope>test</scope>

</dependency>

Setting up our Web-Service

@SpringBootApplication

@EnableAutoConfiguration

@EnableMongoAuditing

@EnableReactiveMongoRepositories

public class ReactiveAccountRestApplication {

public static void main(String[] args) {

SpringApplication.run(ReactiveAccountRestApplication.class, args);

}

}

SpringBootApplication : is a standard configuration for our REST Service. This annotation is equivalent to using @Configuration , @EnableAutoConfiguration and @ComponentScan

: is a standard configuration for our REST Service. This annotation is equivalent to using , and EnableAutoConfiguration : allows our dependencies to do their self-configuration.

: allows our dependencies to do their self-configuration. EnableReactiveMongoRepositories: enables our MongoDB to work reactive.

Model

@Document(collection = "accounts")

public class Account {



@Id

private String id;



@CreatedDate

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy mm:ss")

private Date creationDate;



private Double amount;



private Currency currency;



public Account() {

}



public Account(double amount, Currency currency) {

this.amount = amount;

this.currency = currency;

}



public String getId() {

return id;

}



public void setId(String id) {

this.id = id;

}



public Date getCreationDate() {

return creationDate;

}



public void setCreationDate(Date creationDate) {

this.creationDate = creationDate;

}



public Double getAmount() {

return amount;

}



public Currency getCurrency() {

return currency;

}



public void setCurrency(Currency currency) {

this.currency = currency;

}



public void setAmount(Double amount) {

this.amount = amount;

}



@Override

public String toString() {

return "Account{" +

"id='" + id + '\'' +

", creationDate=" + creationDate +

", amount=" + amount +

", currency=" + currency +

'}';

}

} public enum Currency {

USD, EUR, BRL;



public static Currency fromValue(String value) {

for (Currency currency : values())

if (currency.name().equalsIgnoreCase(value)) {

return currency;

}

return null;

}

}

ReactiveCrudRepository

Now with SPRING 5 we've this new interface from Spring Data, called ReactiveCrudRepository. It's a generic interface with CRUD operations on a repository for a specific type, following reactive paradigms using Project Reactor types which are built on top of Reactive Streams.

@NoRepositoryBean

public interface ReactiveCrudRepository<T, ID> extends Repository<T, ID> {

<S extends T> Mono<S> save(S var1);



<S extends T> Flux<S> saveAll(Iterable<S> var1);



<S extends T> Flux<S> saveAll(Publisher<S> var1);



Mono<T> findById(ID var1);



Mono<T> findById(Publisher<ID> var1);



Mono<Boolean> existsById(ID var1);



Mono<Boolean> existsById(Publisher<ID> var1);



Flux<T> findAll();



Flux<T> findAllById(Iterable<ID> var1);



Flux<T> findAllById(Publisher<ID> var1);



Mono<Long> count();



Mono<Void> deleteById(ID var1);



Mono<Void> deleteById(Publisher<ID> var1);



Mono<Void> delete(T var1);



Mono<Void> deleteAll(Iterable<? extends T> var1);



Mono<Void> deleteAll(Publisher<? extends T> var1);



Mono<Void> deleteAll();

}

Let's extend it and create our repository for Account, ReactiveAccountRepository.



@Repository

public interface ReactiveAccountRepository extends ReactiveCrudRepository<Account, String> {



Flux<Account> findByCurrency(Currency currency);

}

The Spring Framework uses Reactor internally for its own reactive support. Reactor is a Reactive Streams implementation that further extends the basic Reactive Streams Publisher contract with the Flux and Mono composable API types to provide declarative operations on data sequences of 0..N and 0..1 .

Defining our Controller

To define our controller we just need to annotate with @RestController, as we do with spring-mvc. This controller can behave as a simple controller or can be reactive based on the response.

/**

* Created by rodrigo.chaves on 20/06/2017.

*/

@RestController

@RequestMapping("/accounts")

public class AccountController {



private final ReactiveAccountRepository reactiveAccountRepository;



public AccountController(ReactiveAccountRepository reactiveAccountRepository) {

this.reactiveAccountRepository = reactiveAccountRepository;

}



@RequestMapping(value = "/search/bycurrency", method = RequestMethod.GET)

Flux<Account> findByCurrency(@RequestParam String currency) {

return reactiveAccountRepository.findByCurrency(Currency.fromValue(currency));

}



@RequestMapping(value = "/{id}", method = RequestMethod.GET)

Mono<Account> findById(@PathVariable String id) {

return reactiveAccountRepository.findById(id);

}



@RequestMapping(value = "/", method = RequestMethod.POST)

Mono<Account> save(@RequestBody Account account) {

return reactiveAccountRepository.save(account);

}



@RequestMapping(value = "/", method = RequestMethod.GET)

Flux<Account> findAll() {

return reactiveAccountRepository.findAll();

}

}

The Spring Framework exposes Flux and Mono in many of its own reactive APIs. At the application level however, as always, Spring provides choice and fully supports the use of RxJava.

Testing

We can use WebTestClient for testing our server endpoints with an API similar to that of WebClient , and actually delegating to a WebClient instance, but with a focus on testing.

@RunWith(SpringRunner.class)

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)

public class ApplicationIntegrationTest {



WebTestClient webTestClient;





List<Account> expectedAccounts;



@Autowired

ReactiveAccountRepository reactiveAccountRepository;



@Before

public void setup() {

webTestClient = WebTestClient.bindToController(new AccountController(reactiveAccountRepository)).build();

expectedAccounts = reactiveAccountRepository.findAll().collectList().block();

}



@Test

public void findAllAccountsTest() {

this.webTestClient.get().uri("/accounts/")

.accept(APPLICATION_JSON_UTF8)

.exchange()

.expectStatus().isOk()

.expectHeader().contentType(APPLICATION_JSON_UTF8)

.expectBodyList(Account.class).isEqualTo(expectedAccounts);

}



@Test

public void streamAllAccountsTest() throws Exception {

this.webTestClient.get()

.uri("/accounts/")

.accept(TEXT_EVENT_STREAM)

.exchange()

.expectStatus().isOk()

.expectHeader().contentType(TEXT_EVENT_STREAM)

.returnResult(Account.class);

}



@Test

public void streamAllAccountsByCurrencyTest() throws Exception {

this.webTestClient.get()

.uri("/accounts/?currency=EUR")

.accept(TEXT_EVENT_STREAM)

.exchange()

.expectStatus().isOk()

.expectHeader().contentType(TEXT_EVENT_STREAM)

.returnResult(Account.class);

}

}

Performance Test

We can test the performance of our controllers using gatling and at the end we will have a pretty cool chart. The application will receive a total of 1000 Request per second, following this operations CreatAndLoadAccountUSD, CreatAndLoadAccountEUR, CreateAndLoadAccountBR, then LoadAccountByCurrenciesBRL, LoadAccountByCurrenciesUSD and LoadAccountByCurrenciesEUR.

class AccountsLoadTest extends Simulation {

val rampUpTimeSecs = 5

val testTimeSecs = 60

val noOfUsers = 200

val noOfRequestPerSeconds = 1000



val baseURL = "http://localhost:8080"

val accountResourcePath = "/accounts"



object CreatAndLoadAccountUSD {

val create = exec(http("CreateAccountUSD")

.post(accountResourcePath + "/")

.body(RawFileBody("create_account_usd.json"))

.asJSON

.check(jsonPath("$.id").saveAs("accountId")))



val load = exec(http("LoadAccountUSD")

.get(accountResourcePath + "/${accountId}")

.check(status.is(HttpURLConnection.HTTP_OK)))

}



object CreateAndLoadAccountBR {

val create = exec(http("CreatAccountBR")

.post(accountResourcePath + "/")

.body(RawFileBody("create_account_brl.json"))

.asJSON

.check(jsonPath("$.id").saveAs("accountId")))



val load = exec(http("LoadAccountBR")

.get(accountResourcePath + "/${accountId}")

.check(status.is(HttpURLConnection.HTTP_OK)))

}



object CreatAndLoadAccountEUR {

val create = exec(http("CreateAccountEUR")

.post(accountResourcePath + "/")

.body(RawFileBody("create_account_eur.json"))

.asJSON

.check(jsonPath("$.id").saveAs("accountId")))



val load = exec(http("LoadAccountEUR")

.get(accountResourcePath + "/${accountId}")

.check(status.is(HttpURLConnection.HTTP_OK)))

}



object LoadAccountByCurrencies {

val usd = exec(http("LAByCurrencyUSD")

.get(accountResourcePath + "/search/bycurrency?currency=usd")

.check(status.is(HttpURLConnection.HTTP_OK)))

val br = exec(http("LAByCurrencyBR")

.get(accountResourcePath + "/search/bycurrency?currency=br")

.check(status.is(HttpURLConnection.HTTP_OK)))

val eur = exec(http("LAByCurrencyEUR")

.get(accountResourcePath + "/search/bycurrency?currency=eur")

.check(status.is(HttpURLConnection.HTTP_OK)))

}



val httpProtocol = http

.baseURL(baseURL)

.acceptHeader("application/json")

.userAgentHeader("Gatling")



val testScenario = scenario("LoadTest")

.during(testTimeSecs) {

exec(

CreatAndLoadAccountUSD.create,

CreatAndLoadAccountUSD.load,

CreatAndLoadAccountEUR.create,

CreatAndLoadAccountEUR.load,

CreateAndLoadAccountBR.create,

CreateAndLoadAccountBR.load,

LoadAccountByCurrencies.eur,

LoadAccountByCurrencies.usd,

LoadAccountByCurrencies.br

)

}



setUp(

testScenario

.inject(atOnceUsers(noOfUsers)))

.throttle(

reachRps(noOfRequestPerSeconds) in (rampUpTimeSecs seconds),

holdFor(testTimeSecs seconds))

.protocols(httpProtocol)

}

An interesting test would be compare a web-mvc REST application with a reactive REST application + reactive mongo DB. This is our result:

Reactive REST — LoadAccount

Simple REST — LoadAccount

Both tests are identical. LoadAccountUSD reads all the accounts that are USD, Reactive REST we've 1000 plus calls with a better response time ( t < 800 ms). The main reason of this result is that we're using a Reactive MongoDB. This example from Reactive Mongo, illustrates well what we are seeing:

Imagine that you have a web application with 10 concurrent accesses to the database. That means you eventually end up with 10 frozen threads at the same time, doing nothing but waiting for a response. A common solution is to rise the number of running threads to handle more requests. Such a waste of resources is not really a problem if your application is not heavily loaded, but what happens if you have 100 or even 1000 more requests to handle, performing each several DB queries? The multiplication grows really fast.

Conclusion

Spring 5 will change how we build our REST APIs, but lots of work is still needed to deliver a stable version of web-flux. Problems that I faced writing this services:

API Documentation: springfox still does not support web-flux, but is in their plain to support it. details

springfox still does not support web-flux, but is in their plain to support it. details Spring Security: still not supported.

still not supported. Documentation: we are still on a release candidate, the documentation it's improving but still not mature.

My recommendation is, if you want to use web-flux as soon the GA version is released, take in consideration what you will gain and lose with this new approach.

This example can be download on: https://github.com/LINKIT-Group/spring-rest-reactive