Spring Boot: Server-Sent Events
Spring 5 will see support for asynchronous and Reactive apps. Here's how to implement one new tool: server-sent events in your apps.
Join the DZone community and get the full member experience.
Join For FreeSpring 5, which will release later this year, will support building asynchronous and Reactive applications. To learn more about the features in Spring 5 and Reactive programming, you can refer to this article. I'll highly suggest you do that before continuing on.
Spring 5 will make it easier to implement Server-Sent Events (SSE), and we'll see, with the help of a simple stock market simulation, how to implement SSE.
Server-Sent Events (SSE)
SSE is a web technology where a browser receives updates from a server via an HTTP connection. It is better than polling because polling has a lot of HTTP overhead. Unlike WebSockets, however, it is unidirectional (server to browser).
SSEs are sent over traditional HTTP, hence they don't require any special implementation on the server.
Dependencies
We'll use Gradle to build our project. I recommend using Spring Initializr to bootstrap your project.
We'll use:
- Spring Boot 2
- Spring Webflux
- Lombok
Not all the Spring libraries have a stable release yet.
buildscript {
ext {
springBootVersion = '2.0.0.BUILD-SNAPSHOT'
}
...
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-webflux')
compileOnly('org.projectlombok:lombok')
...
}
Stock Market Example
In this example, we'll simulate stock market transactions and notify the client about them.
The steps involved are:
- Initialize stocks with their prices
- After every second, update stock prices and make stock transactions.
- The client will receive the details of the stock transactions after every second.
Let's have a look at the Stock class.
class Stock {
String name;
float price;
}
We'll initialize some random Stocks at the start of the application.
Let's have a look at the StockTransaction class.
class StockTransaction {
String user;
Stock stock;
Date when;
}
Reactive SSE
We'll create a service that returns a Flux
of StockTransactions.
@Service
class StockTransactionService {
Flux<StockTransaction> getStockTransactions() {
Flux<Long> interval = Flux.interval(Duration.ofSeconds(1));
interval.subscribe((i) -> stockList.forEach(stock -> stock.setPrice(changePrice(stock.getPrice()))));
Flux<StockTransaction> stockTransactionFlux = Flux.fromStream(Stream.generate(() -> new StockTransaction(getRandomUser(), getRandomStock(), new Date())));
return Flux.zip(interval, stockTransactionFlux).map(Tuple2::getT2);
}
}
We are creating a Flux
that generates a long value every second. We are then updating the price of every stock. We are creating another Flux
that creates a StockTransaction. The Flux.zip
method is taking both these Flux
es and combining them. They'll be combined in a strict sequence (when both Flux
es have emitted their nth item). We are then returning the second Flux
. Hence, the resulting Flux
will emit a StockTransaction after every second.
Web API
We'll create an endpoint GET /stock/transaction that will continuously fetch details of the latest transactions happening.
@RestController
@RequestMapping("/stock/transaction")
class StockTransactionController {
@Autowired
StockTransactionService stockTransactionService;
@GetMapping(produces = MediaType.APPLICATION_STREAM_JSON_VALUE)
public Flux<StockTransaction> stockTransactionEvents(){
return stockTransactionService.getStockTransactions();
}
}
MediaType.APPLICATION_STREAM_JSON_VALUE
signifies that the server will send SSEs.
This API will return a response with the header Content-Type: application/stream+json
.
The cURL command for testing this is:
curl -v http://localhost:8080/stock/transaction
.
The output obtained will look like this
{"user":"adam","stock":{"name":"guava","price":32.73},"when":1505049586252}
{"user":"tom","stock":{"name":"infinity","price":39.97},"when":1505049587291}
{"user":"mike","stock":{"name":"mango","price":36.12},"when":1505049588253}
{"user":"mike","stock":{"name":"mango","price":37.93},"when":1505049589253}
{"user":"tom","stock":{"name":"mango","price":39.83},"when":1505049590253}
...
Conclusion
I have tried explaining, with a simple example, how to send SSEs using Spring Boot. For more information, read up about Project Reactor.
You can find the complete example on GitHub.
Published at DZone with permission of Mohit Sinha, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments