Sunday, June 23, 2019

Reactive web application with SpringBoot

0. Intro

Event-driven asynchronous approach is getting more and more popular.
When we're talking about REACTIVE we means that we should react somehow on something. When it comes to web applications, most often that means: we should react by showing some new information on the page on some event,

With traditional approach web page should poll(send request, receive response) some rest service every time it want to get new portion of data:




With reactive - we don't have to poll, we just have to subscribe to some "event stream".

 


1. SpringBoot reactive web application

Let's now create "reactive" springBoot application.

build.gradle:

plugins {
 id 'org.springframework.boot' version '2.1.5.RELEASE'
 id 'java'
}

apply plugin: 'io.spring.dependency-management'

group = 'com.demien'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

configurations {
 developmentOnly
 runtimeClasspath {
  extendsFrom developmentOnly
 }
}

repositories {
 mavenCentral()
}

dependencies {
 implementation 'org.springframework.boot:spring-boot-starter-webflux'
 developmentOnly 'org.springframework.boot:spring-boot-devtools'
 testImplementation 'org.springframework.boot:spring-boot-starter-test'
 testImplementation 'io.projectreactor:reactor-test'
}

- we have to add dependency "webflux" here.



Main application runner: 

package com.demien.reactweb;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ReactwebApplication {

 public static void main(String[] args) {
  SpringApplication.run(ReactwebApplication.class, args);
 }

}


- nothing interesting is here, just start of spring boot application.

Reactive rest controller: 

package com.demien.reactweb;

import java.time.Duration;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import reactor.core.publisher.Flux;

@RestController
public class RandomNumberController {

 private final Log log = LogFactory.getLog(RandomNumberController.class);

 @RequestMapping("/random")
 public Flux<Integer> random() {
  return Flux.interval(Duration.ofSeconds(5)).map(i -> {
   this.log.info("iteration:" + i);
   return generateRandomNumber();
  }).log();
 }

 private int generateRandomNumber() {
  return (int) (Math.random() * 1000);
 }

}

-magic is happening here ! First of all, we're returning Flux<Integer> - that means, we're actually returning "stream" of integers. Second - we're using delay of 5 seconds between emitting elements.

2. HTML Web page

Here we have just one button. But than we're pressing it, it subscribes as to events which are coming from rest endpoint "/random" which we created at previous step. Also we're defining the "handler" (stringEventSource.onmessage) - what should we do when new event arrives: we're just adding one more list item.


<html>

<head>

    <script>
        function registerEventSourceAndAddResponseTo(uri, elementId) {
            var stringEvents = document.getElementById(elementId);
            var stringEventSource = new EventSource(uri);
            stringEventSource.onmessage = function (e) {
                var newElement = document.createElement("li");
                newElement.innerHTML = e.data;
                stringEvents.appendChild(newElement);
            }
        }

        function subscribe() {
            registerEventSourceAndAddResponseTo("http://localhost:8080/random","display");
        }

    </script>

</head>

<body>

    <p>
        <button id="subscribe-button" onclick="subscribe()">Subscribe to random numbers</button>
        <ul id="display"></ul>
    </p>


</body>

</html>

3. Let's run it! 

I'm running my springBoot application and opening the web page:


Nothing happen so far. We have to press the button for being subscribed to new event. Let's press it:



And now it's much better! Events are coming from server side and UI shows them!

4. The end. 

Full source code can be downloaded from here.

No comments:

Post a Comment