Contents
- What you’ll learn
- Additional prerequisites
- Getting started
- Setting up SSE in the bff service
- Configuring the Kafka connector for the bff service
- Configuring the frontend service to subscribe to and consume events
- Building and running the application
- Tearing down the environment
- Great work! You’re done!
- Related Links
- Guide Attribution
Tags
Streaming updates to a client using Server-Sent Events
Prerequisites:
Learn how to stream updates from a MicroProfile Reactive Messaging service to a front-end client by using Server-Sent Events (SSE).
What you’ll learn
You will learn how to stream messages from a MicroProfile Reactive Messaging service to a front-end client by using Server-Sent Events (SSE).
MicroProfile Reactive Messaging provides an easy way for Java services to send requests to other Java services, and asynchronously receive and process the responses as a stream of events. SSE provides a framework to stream the data in these events to a browser client.
What is SSE?
Server-Sent Events is an API that allows clients to subscribe to a stream of events that is pushed from a server. First, the client makes a connection with the server over HTTP. The server continuously pushes events to the client as long as the connection persists. SSE differs from traditional HTTP requests, which use one request for one response. SSE also differs from Web Sockets in that SSE is unidirectional from the server to the client, and Web Sockets allow for bidirectional communication.
For example, an application that provides real-time stock quotes might use SSE to push price updates from the server to the browser as soon as the server receives them. Such an application wouldn’t need Web Sockets because the data travels in only one direction, and polling the server by using HTTP requests wouldn’t provide real-time updates.
The application that you will build in this guide consists of a frontend
service, a bff
(backend for frontend) service, and three instances of a system
service. The system
services periodically publish messages that contain their hostname and current system load. The bff
service receives the messages from the system
services and pushes the contents as SSE to a JavaScript client in the frontend
service. This client uses the events to update a table in the UI that displays each system’s hostname and its periodically updating load. The following diagram depicts the application that is used in this guide:
In this guide, you will set up the bff
service by creating an endpoint that clients can use to subscribe to events. You will also enable the service to read from the reactive messaging channel and push the contents to subscribers via SSE. After that, you will configure the Kafka connectors to allow the bff
service to receive messages from the system
services. Finally, you will configure the client in the frontend
service to subscribe to these events, consume them, and display them in the UI.
To learn more about the reactive Java services that are used in this guide, check out the Creating reactive Java microservices guide.
Additional prerequisites
You will build and run the services in Docker containers. You can learn more about containerizing services with Docker in the Containerizing microservices guide.
Install Docker and start your Docker environment by following the instructions from Docker.
Getting started
The fastest way to work through this guide is to clone the Git repository and use the projects that are provided inside:
git clone https://github.com/openliberty/guide-reactive-messaging-sse.git
cd guide-reactive-messaging-sse
The start
directory contains the starting project that you will build upon.
The finish
directory contains the finished project that you will build.
Before you begin, make sure you have all the necessary prerequisites.
Setting up SSE in the bff service
In this section, you will create a REST API for SSE in the bff
service. When a client makes a request to this endpoint, the initial connection between the client and server is established and the client is subscribed to receive events that are pushed from the server. Later in this guide, the client in the frontend
service uses this endpoint to subscribe to the events that are pushed from the bff
service.
Additionally, you will enable the bff
service to read messages from the incoming stream and push the contents as events to subscribers via SSE.
Navigate to the start
directory to begin.
Create the BFFResource class.
bff/src/main/java/io/openliberty/guides/bff/BFFResource.java
BFFResource.java
Creating the SSE API endpoint
The subscribeToSystem()
method allows clients to subscribe to events via an HTTP GET
request to the /bff/sse/
endpoint. The @Produces(MediaType.SERVER_SENT_EVENTS)
annotation sets the Content-Type
in the response header to text/event-stream
. This content type indicates that client requests that are made to this endpoint are to receive Server-Sent Events. Additionally, the method parameters take in an instance of the SseEventSink
class and the Sse
class, both of which are injected using the @Context
annotation. First, the method checks if the sse
and broadcaster
instance variables are assigned. If these variables aren’t assigned, the sse
variable is obtained from the @Context
injection and the broadcaster
variable is obtained by using the Sse.newBroadcaster()
method. Then, the register()
method is called to register the SseEventSink
instance to the SseBroadcaster
instance to subscribe to events.
For more information about these interfaces, see the Javadocs for OutboundSseEvent and OutboundSseEvent.Builder.
Reading from the reactive messaging channel
The getSystemLoadMessage()
method receives the message that contains the hostname and the average system load. The @Incoming("systemLoad")
annotation indicates that the method retrieves the message by connecting to the systemLoad
channel in Kafka, which you configure in the next section.
Each time a message is received, the getSystemLoadMessage()
method is called, and the hostname and system load contained in that message are broadcasted in an event to all subscribers.
Broadcasting events
Broadcasting events is handled in the broadcastData()
method. First, it checks whether the broadcaster
value is null
. The broadcaster
value must include at least one subscriber or there’s no client to send the event to. If the broadcaster
value is specified, the OutboundSseEvent
interface is created by using the Sse.newEventBuilder()
method, where the name
of the event, the data
it contains, and the mediaType
are set. The OutboundSseEvent
interface is then broadcasted, or sent to all registered sinks, by invoking the SseBroadcaster.broadcast()
method.
You just set up an endpoint in the bff
service that the client in the frontend
service can use to subscribe to events. You also enabled the service to read from the reactive messaging channel and broadcast the information as events to subscribers via SSE.
Configuring the Kafka connector for the bff service
A complete system
service is provided for you in the start/system
directory. The system
service is the producer of the messages that are published to the Kafka messaging system. The periodically published messages contain the system’s hostname and a calculation of the average system load (its CPU usage) for the last minute.
Configure the Kafka connector in the bff
service to receive the messages from the system
service.
Create the microprofile-config.properties file.
bff/src/main/resources/META-INF/microprofile-config.properties
microprofile-config.properties
The bff
service uses an incoming connector to receive messages through the systemLoad
channel. The messages are then published by the system
service to the system.load
topic in the Kafka message broker. The key.deserializer
and value.deserializer
properties define how to deserialize the messages. The group.id
property defines a unique name for the consumer group. All of these properties are required by the Apache Kafka Consumer Configs documentation.
Configuring the frontend service to subscribe to and consume events
In this section, you will configure the client in the frontend
service to subscribe to events and display their contents in a table in the UI.
The front-end UI is a table where each row contains the hostname and load of one of the three system
services. The HTML and styling for the UI is provided for you but you must populate the table with information that is received from the Server-Sent Events.
Create the index.js file.
frontend/src/main/webapp/js/index.js
index.js
Subscribing to SSE
The initSSE()
method is called when the page first loads. This method subscribes the client to the SSE by creating a new instance of the EventSource
interface and specifying the http://localhost:9084/bff/sse
URL in the parameters. To connect to the server, the EventSource
interface makes a GET
request to this endpoint with a request header of Accept: text/event-stream
.
Because this request comes from localhost:9080
and is made to localhost:9084
, it must follow the Cross-Origin Resource Sharing (CORS) specification to avoid being blocked by the browser. To enable CORS for the client, set the withCredentials
configuration element to true in the parameters of the EventSource
interface. CORS is already enabled for you in the bff
service. To learn more about CORS, check out the CORS guide.
Consuming the SSE
The EventSource.addEventListener()
method is called to add an event listener. This event listener listens for events with the name of systemLoad
. The systemLoadHandler()
function is set as the handler function, and each time an event is received, this function is called. The systemLoadHandler()
function will take the event object and parse the event’s data property from a JSON string into a JavaScript object. The contents of this object are used to update the table with the system hostname and load. If a system is already present in the table, the load is updated, otherwise a new row is added for the system.
Building and running the application
To build the application, navigate to the start
directory and run the following Maven install
and package
goals from the command line:
mvn -pl models install
mvn package
Run the following commands to containerize the frontend
, bff
, and system
services:
docker build -t frontend:1.0-SNAPSHOT frontend/.
docker build -t bff:1.0-SNAPSHOT bff/.
docker build -t system:1.0-SNAPSHOT system/.
Next, use the following startContainers.sh
script to start the application in Docker containers:
WINDOWS
MAC
LINUX
.\scripts\startContainers.bat
./scripts/startContainers.sh
This script creates a network for the containers to communicate with each other. It also creates containers for Kafka, the frontend
service, the bff
service , and three instances of the system
service.
The application might take some time to get ready. See the http://localhost:9084/health URL to confirm that the bff
microservice is up and running.
Once your application is up and running, open your browser and check out your frontend
service by going to http://localhost:9080.
The latest version of most modern web browsers supports Server-Sent Events. The exception is Internet Explorer, which does not support SSE. When you visit the URL, look for a table similar to the following example:
The table contains three rows, one for each of the running system
containers. If you can see the loads updating, you know that your bff
service is successfully receiving messages and broadcasting them as SSE to the client in the frontend
service.
Tearing down the environment
Run the following script to stop the application:
WINDOWS
MAC
LINUX
.\scripts\stopContainers.bat
./scripts/stopContainers.sh
Great work! You’re done!
You developed an application that subscribes to Server-Sent Events by using MicroProfile Reactive Messaging, Open Liberty, and Kafka.
Related Links
Guide Attribution
Streaming updates to a client using Server-Sent Events by Open Liberty is licensed under CC BY-ND 4.0
Prerequisites:
Nice work! Where to next?
What did you think of this guide?
Thank you for your feedback!
What could make this guide better?
Raise an issue to share feedback
Create a pull request to contribute to this guide
Need help?
Ask a question on Stack Overflow