Building a Real-Time Crypto Price Simulator with Spring Boot
Written on
Chapter 1: Introduction to the Crypto Price Simulator
In this article, we will develop a Crypto Price Simulator that consists of two distinct Spring Boot applications: the Crypto Price Producer and the Crypto Price Consumer. The Producer generates updates for cryptocurrency prices, with a focus on Bitcoin (BTC), and broadcasts these updates via WebSocket. Meanwhile, the Consumer connects to the Producer's WebSocket to receive and process the price changes.
If you're interested in creating a React Consumer for this Crypto Price Simulator, check out:
Real-time Crypto Price Simulator: Implementing a React Consumer App
Step-by-step guide to building a React App that consumes real-time updates from the Spring Boot Producer's WebSocket.
For those looking to run the Crypto Price Simulator on Minikube (Kubernetes), we provide detailed instructions here:
Real-time Crypto Price Simulator: Running in Minikube (Kubernetes)
Exploring how to deploy the Crypto Price Producer, Consumer, and WebSocket functionality in a Kubernetes environment.
Now, let’s get started!
WebSocket, SockJS, and STOMP Overview
This article delves into more than just WebSocket; it also incorporates SockJS and STOMP. Together, these three components create a powerful framework for real-time web communication.
Here’s a brief overview of each:
- WebSocket: A protocol that facilitates full-duplex communication between a client and a server over a single, persistent connection, enabling real-time data exchange.
- SockJS: Functions as both a JavaScript library and a protocol. As a library, it abstracts WebSocket for a consistent API experience, while as a protocol, it sets communication rules and fallback options for environments lacking robust WebSocket support.
- STOMP (Simple Text Oriented Messaging Protocol): A lightweight messaging protocol designed for the WebSocket architecture. STOMP provides a higher-level abstraction over WebSocket, featuring capabilities like message routing, publish-subscribe models, and point-to-point communication.
Prerequisites
To follow along with this tutorial, ensure you have Java 17 or higher installed on your machine.
Creating the Project Structure
- Create a Directory: Set up a folder named websocket-crypto-simulator in your workspace where we will develop the Producer and Consumer applications.
Creating the Crypto Price Producer Spring Boot Application
We will initiate our Spring Boot application using Spring Initializr. Name the application crypto-price-producer and include the following dependencies: Spring Web and WebSocket. We will utilize Spring Boot version 3.2.4 with Java 17. Follow this [link](#) for all setup details.
After clicking the GENERATE button, download the zip file, extract it into the websocket-crypto-simulator folder, and open the crypto-price-producer project in your IDE.
Organizing Code with Packages
To maintain an organized code structure, create the following packages within the com.example.cryptopriceproducer root package: simulator and websocket.
Defining the CryptoUpdate Record
In the simulator package, define the CryptoUpdate record with the following code:
package com.example.cryptopriceproducer.simulator;
public record CryptoUpdate(String crypto, String price, String datetime) {
}
This record encapsulates the data sent through the WebSocket.
Implementing the CryptoSimulator Class
Next, in the simulator package, create the CryptoSimulator class with the provided implementation:
package com.example.cryptopriceproducer.simulator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.security.SecureRandom;
import java.text.NumberFormat;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.Random;
@EnableScheduling
@Component
public class CryptoSimulator {
private final SimpMessagingTemplate simpMessagingTemplate;
public CryptoSimulator(SimpMessagingTemplate simpMessagingTemplate) {
this.simpMessagingTemplate = simpMessagingTemplate;}
@Scheduled(fixedRate = 1000)
public void simulateTrade() {
if (hasTrade()) {
double price = rand.nextDouble() * 100;
String priceInDollar = priceFormatter.format(price);
String dateTimeFormatted = dateTimeFormatter.format(Instant.now());
CryptoUpdate cryptoUpdate = new CryptoUpdate("BTC", priceInDollar, dateTimeFormatted);
log.info("Publishing crypto change: {}", cryptoUpdate);
simpMessagingTemplate.convertAndSend("/topic/prices", cryptoUpdate);
}}
private boolean hasTrade() {
return rand.nextBoolean();}
private static final Logger log = LoggerFactory.getLogger(CryptoSimulator.class);
private static final Random rand = new SecureRandom();
private static final NumberFormat priceFormatter = NumberFormat.getCurrencyInstance(Locale.US);
private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
.withZone(ZoneId.systemDefault());
}
This class simulates updates in crypto prices and publishes them to a WebSocket topic. The simulateTrade() method runs every second, and the hasTrade() method determines whether a trade occurred. It constructs CryptoUpdate objects with random prices and sends them to the /topic/prices destination.
Configuring WebSocket with WebSocketConfig Class
In the websocket package, create the WebSocketConfig class with the following content:
package com.example.cryptopriceproducer.websocket;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws")
.setAllowedOriginPatterns("*")
.withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.enableSimpleBroker("/topic");
}
}
This class sets up WebSocket configurations. The @EnableWebSocketMessageBroker annotation enables WebSocket message handling, including higher-level messaging protocols.
Creating the Crypto Price Consumer Spring Boot Application
Now, we will create another Spring Boot application using Spring Initializr, naming it crypto-price-consumer. Include the dependencies: Spring Web and Thymeleaf, using the same version settings as before.
Creating app.js
Within the src/main/resources/static directory, create the app.js file with the following content:
function connectToWebSocket(uri) {
const url = uri + "/ws";
const socket = new SockJS(url);
const stompClient = Stomp.over(socket);
stompClient.connect({},
function (frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/prices', function (cryptoUpdate) {
console.log(cryptoUpdate);
const cryptoUpdateBody = JSON.parse(cryptoUpdate.body);
const row = '<tr>' +
'<td>' + cryptoUpdateBody.datetime + '</td>' +
'<td>' + cryptoUpdateBody.crypto + '</td>' +
'<td>' + cryptoUpdateBody.price + '</td>' +
'</tr>';
$('table tbody').prepend(row);
});
},
function() {
alert('Unable to connect to Websocket!');}
);
}
This JavaScript function establishes a WebSocket connection to a provided URI, subscribing to the /topic/prices topic upon successful connection. It dynamically adds new crypto updates to the HTML table and alerts the user in case of a connection failure.
Creating the index.html File
In the src/main/resources/templates directory, create the index.html file with the following content:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Bitcoin-Client</title>
</head>
<body>
<div class="ui text container">
<h1 class="ui center aligned header">Crypto Price</h1>
<table class="ui compact table">
<thead>
<tr>
<th>Date/Time</th>
<th>Crypto</th>
<th>Price</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</body>
<script src="/app.js"></script>
<script th:inline="javascript">
$(function() {
const uri = "[(${@environment.getProperty('crypto-prices-producer.ws.uri')})]";
connectToWebSocket(uri);
});
</script>
</html>
This HTML document contains a header and a table for displaying crypto update information. The connection to the Crypto Price Producer WebSocket is initiated through a script at the bottom.
Updating application.properties
Modify the application.properties file to include:
server.port=9080
crypto-prices-producer.ws.uri=http://localhost:8080
Here’s a brief explanation of each property:
- server.port: Defines the application's port.
- crypto-prices-producer.ws.uri: Specifies the URI for the Crypto Price Producer WebSocket.
Demonstration of the Simulator
To start the Crypto Price Producer and Consumer applications, run the following commands in separate terminal windows:
# For the Producer
./mvnw clean spring-boot:run
# For the Consumer
./mvnw clean spring-boot:run
Visualizing Price Updates
Open your browser and navigate to http://localhost:9080. You should see the crypto price updates:
Shutting Down
To stop the applications, press Ctrl+C in the terminal windows where the Crypto Price Producer and Consumer are running.
Conclusion
In this article, we successfully created a Crypto Price Simulator utilizing two Spring Boot applications: the Crypto Price Producer and the Crypto Price Consumer. The Producer simulates price updates for Bitcoin and shares them via WebSocket, while the Consumer connects to track these updates. We concluded with a demonstration of the Crypto Price Simulator in action.
Additional Readings
- RSocket Crypto Server Tutorial
The video titled "Real-Time Crypto Price using JS and WebSockets | Binance Websocket Market Streams API" provides insights into implementing WebSocket for real-time crypto price updates.
Support and Engagement
If you found this article helpful, consider supporting by:
- Engaging through claps, highlights, and comments—I’m here to answer any questions you may have.
- Sharing this article on your social media platforms.
- Following me on Medium, LinkedIn, Twitter, and GitHub.
- Subscribing to my newsletter to stay updated with my latest posts.