Client-to-Server Messaging in Lightstreamer Explained

Lightstreamer Broker excels not only as a real-time data streaming platform for web, mobile, and desktop applications, but also provides robust client-to-server messaging capabilities. This dual functionality positions Lightstreamer as a comprehensive solution for applications requiring both continuous data updates and interactive communication features.

Understanding how these two communication paradigms differ — yet complement each other — is essential for leveraging Lightstreamer’s full potential in your applications.

Data streaming vs. client-to-server messaging

Real-time server-to-client data streaming and client-to-server messaging serve fundamentally different purposes and operate under distinct design principles.

In the real-time streaming model, the server takes an active role by continuously pushing data updates to a connected client. This setup is common in situations where information changes rapidly — such as stock ticker updates, live sports scores, or sensor readings — and clients need to be in sync with the latest state without having to actively request every update.

On the other hand, client-to-server messaging is characterized by its client-initiated, discrete communication events. Rather than receiving a constant stream of information, the client composes and sends a message — be it a chat entry, a command, or an event signal — whenever an action is required. Here, the emphasis is on the prompt and reliable handling of individual messages. Unlike streaming, which is continuous and managed primarily by the server’s schedule, client-to-server messaging focuses on ensuring that each message the client sends reaches the server intact and is processed immediately. Such interactions often involve an acknowledgment mechanism or direct responses, making them suitable for interactive applications where feedback is as important as the message content itself.

Together, these two approaches address different communication needs. Real-time server-to-client streaming excels at delivering a constant, ordered flow of data updates suited for monitoring and broadcasting scenarios, while client-to-server messaging prioritizes responsive interaction and discrete action triggers. Understanding these differences is crucial when designing systems that rely on both continuous awareness and immediate, responsive communication.

Lightstreamer architecture

Lightstreamer features a layered, modular architecture. At its core is the Lightstreamer Broker, a high-performance engine that manages all client connections and ensures efficient data delivery, regardless of varying network conditions.

A key aspect of this architecture is its adapter-based approach. While Data Adapters serve as connectors, feeding real-time information from backend sources into the Lightstreamer Broker, the Metadata Adapter acts as a supervisor that intercepts, validates and manages client requests. It enforces business rules, handles authentication and authorization, and oversees quality-of-service policies, ensuring that only legitimate, well-formed data is processed further. Furthermore, the Metadata Adapter determines whether to accept or refuse client-to-server messages and whether to send a response back to the client.

Having clarified the roles of the two major components — the Client and the Metadata Adapter — in client-to-server messaging, we can now delve into the topic of messaging modes.

Client-to-server messaging

Lightstreamer offers three distinct messaging modes that allow developers to tailor their communication strategies based on the particular balance of reliability and performance required by their application. Here’s how each mode differs to cater to these needs:

  1. In-Order Processing: This mode ensures that messages are processed in the exact order they are sent by assigning each message to a specific sequence. The server maintains strict ordering guarantees, which is essential for applications where sequence and state consistency are critical (e.g., financial transactions or actual workflows).
  2. Unordered Processing: In this mode, messages bypass the strict ordering mechanism. Each message is treated as an independent unit, allowing them to be processed immediately as they arrive.
  3. Fire-and-Forget: This mode takes performance to the extreme by completely omitting the delivery verification process. The server does not confirm whether a message is received, nor does it check for out-of-sequence or duplicate messages.

Below, we illustrate in detail the three messaging modes available in Lightstreamer.

This in-depth exploration will help clarify the unique trade-offs and operational considerations of each mode, enabling you to choose the best option for your specific needs.

In-order processing

Each message is assigned to a specific topic — referred to as a “sequence” in Lightstreamer terminology. The server guarantees that messages within the same sequence are processed in the precise order in which they are sent, thereby ensuring in-order processing. In contrast, messages assigned to different sequences are processed independently and may not follow a specific order.

In interactive applications where feedback is as critical as the message content itself, developers can attach listeners to messages that require acknowledgment. The client library guarantees that these listeners will be triggered in the same order as messages are acknowledged by the server.

This ordered listener execution complements the in-order processing guarantee provided by the server, creating an end-to-end ordered messaging system for applications that require strict sequential operations.

Discarded messages

While Lightstreamer guarantees in-order processing for messages within the same sequence, network conditions can sometimes interfere with message delivery. This is particularly true for HTTP transport, which lacks the persistent connection advantages of WebSockets.

To prevent indefinite blocking of an entire message sequence when preceding messages are delayed, Lightstreamer implements a timeout mechanism through the delayTimeout parameter. Each message can be configured with a specific timeout value, defining the maximum duration it will wait for its predecessors in the sequence. If this time limit is exceeded, any pending messages that are blocking the sequence are discarded, allowing the processing to continue without interruption.

When a message is discarded, the client library notifies the application through the ClientMessageListener#onDiscarded callback, enabling appropriate handling of these exceptional cases. This mechanism ensures system responsiveness even under challenging network conditions.

It’s important to note that setting an extremely low or zero delayTimeout may result in premature message discarding, even when no significant network issues exist. This occurs because even minimal processing delays could exceed such aggressive timeout thresholds. Therefore, timeout values should be carefully calibrated based on the application’s specific requirements for responsiveness versus delivery reliability.

Unordered processing

For applications where processing speed and throughput take precedence over strict message ordering, Lightstreamer provides the UNORDERED_MESSAGES special sequence identifier. Messages assigned to this sequence bypass the standard ordering guarantees, allowing for immediate processing regardless of other messages’ status.

When using unordered processing, each message is treated as an independent unit rather than part of a sequential chain. This approach eliminates potential delays caused by waiting for preceding messages in a sequence, directly benefiting latency-sensitive applications where each message carries self-contained information.

This mode is particularly valuable in high-frequency scenarios where:

  • Messages have no causal dependencies on one another
  • Transmission speed outweighs strict chronological delivery
  • Network efficiency needs to be maximised

Even when in-order processing is relaxed, the client library guarantees the exactly-once delivery of every message within a topic. If a message fails to reach the server within the configured delayTimeout while subsequent messages have been successfully delivered, the server may discard that delayed message to ensure continued system performance.

Discarding messages in this mode might initially appear counterintuitive. However, since a listener can be attached to each message, the delayTimeout parameter ensures that the application is safeguarded against waiting indefinitely for a notification.

Fire and forget

If exactly-once delivery guarantees are not required, a fire-and-forget mode is available. In this mode, the server does not verify the receipt of messages — omitting checks for missing, duplicate, or out-of-sequence messages — while the client library assumes that each sent message reliably reaches the server. This approach maximises throughput by eliminating the overhead associated with delivery verification.

Summary of the modes

The message sending feature offers three operational modes that balance reliability and performance. These modes are arranged, from highest reliability (and lowest performance) to highest efficiency (and lowest reliability), as follows:

  • In-Order Processing
  • Unordered Processing
  • Fire-and-Forget

After gaining a solid understanding of the three messaging modes, we can now explore how the Lightstreamer API brings these concepts to life. We begin by examining the client-side API and then move on to the server-side, effectively bridging the gap between theoretical models and practical application.

Client API

To send a message to the server, you must invoke the sendMessage method of the LightstreamerClient class.

The method signature (for the Web Client SDK) is:

sendMessage(msg, sequence, delayTimeout, listener, enqueueWhileDisconnected)

The sending mode is determined by the specific values provided for the sequence and listener parameters, as illustrated below.

ModeSequenceListener
In-Order ProcessingAny value other than “UNORDERED_MESSAGES”Optional
Unordered Processing“UNORDERED_MESSAGES”Required
Fire-and-Forget“UNORDERED_MESSAGES”None

To activate In-Order Processing, you must use a sequence identifier other than the special string UNORDERED_MESSAGES. A listener can be attached to handle asynchronous callbacks when the message is processed, though it isn’t mandatory.

// Example: sending an in-ordered message (the listener argument is optional)

client.sendMessage("hello", "greetings", null, {
  onProcessed: () => console.log("processed")
})

To activate Unordered Processing, every message must be assigned the sequence identifier UNORDERED_MESSAGES, signalling that strict order is not required. Additionally, a listener is required with this mode, meaning that each message must have an associated callback to monitor its outcome.

// Example: sending an unordered message (the listener argument is required)

client.sendMessage("hello", "UNORDERED_MESSAGES", null, {
  onProcessed: () => console.log("processed")
})

Like Unordered Processing, Fire-and-Forget also uses the UNORDERED_MESSAGES identifier, emphasising that the messages are not part of a strict sequence. However, in Fire-and-Forget mode, no listener is attached, meaning that once the message is sent, the system does not track its delivery or processing outcome.

// Example: sending a fire-and-forget message (the listener argument must be omitted)

client.sendMessage("hello", "UNORDERED_MESSAGES")

The other parameters are:

  • delayTimeout: This parameter sets the maximum time (in milliseconds) the server will wait for a message in a sequence if preceding messages are delayed. Once this time limit is reached, any pending messages blocking the sequence are discarded. Note that this parameter is ignored in Fire-and-Forget mode. If not specified, the server’s default timeout configuration will be applied.
  • enqueueWhileDisconnected: If set to true, this flag allows the message to be queued when the client is disconnected instead of being immediately aborted. The message will wait for a new session to be established; however, it may still be aborted later if conditions require it. If the flag is not provided, messages will not be enqueued.

Message sending outcomes

The ClientMessageListener is an interface designed to handle asynchronous notifications about the outcomes of messages sent via the sendMessage method.

Implementing the ClientMessageListener allows you to define custom behaviour for a range of outcomes:

  • onProcessed: Invoked when the server successfully processes the message. This callback typically provides any response from the Metadata Adapter, confirming that the message was handled as expected.
  • onError: Called when the server processes the message but a generic error occurs during its handling, indicating that the processing has failed despite the message reaching the server.
  • onDeny: Triggered when the server refuses to process the message (for example, due to specific rules defined by the Metadata Adapter). This method not only flags that the processing wasn’t successful but also provides an error code and a descriptive message.
  • onDiscarded: Executed when the server discards a message. This means the message hasn’t reached the Metadata Adapter, and the subsequent messages in the sequence are then enabled for processing. In unordered processing mode, this callback typically signals that the client library failed to deliver the message within the configured delayTimeout.
  • onAbort: This event is triggered when the outcome of the message processing cannot be determined. It typically occurs when the session is terminated before the message is sent or a definitive response is received from the server.

Aborted messages

Typically, a sequence of messages is only meaningful within the session from which it originates. When a session ends — either explicitly by the user or automatically by the client library in response to an unrecoverable error — any remaining unsent messages (i.e. aborted messages) can be safely disregarded. However, some applications require message streams to span across session boundaries. This scenario introduces a few challenges, as the in-order, exactly-once guarantees discussed earlier apply solely to messages within the same session.

Consequently, if an aborted message must be resent in a new session, you must carefully consider the following:

  1. Duplicates: You must ensure that your MetadataAdapter can handle duplicate messages. Even if an onAbort event occurs, the original transmission might have actually succeeded, so your system needs to be resilient against unintended duplication.
  2. Message Order: If multiple messages are pending when the client aborts them, onAbort events may not be fired in the same order the messages were submitted. Your application must manage these aborted messages carefully to ensure they are processed in the correct sequence.

In summary, while retransmitting messages in the next session can be an effective strategy for maintaining continuity, it introduces additional complexity. The most appropriate approach depends on the unique requirements of your application, especially regarding duplicate handling and message order integrity.

Metadata Adapter API

On the server, the messages are handled by the notifyUserMessage method of the MetadataProvider interface.

The method signature (for the In-Process Java Adapter SDK) is:

CompletableFuture<String> notifyUserMessage(String user, String sessionID, String message)
  throws CreditsException, NotificationException

Its primary role is to forward the message to the Metadata Adapter, which interprets it and determines whether to send a response, return null if no response is needed, or refuse the message altogether. The method returns a CompletableFuture<String> that is eventually completed with either a valid response or an exception.

If there are issues with the user’s credentials or session, an exception may be thrown immediately within the notifyUserMessage method or later by rejecting the CompletableFuture. When a CreditsException occurs, its error code and message are communicated to the client via the ClientMessageListener#onDeny callback. Conversely, if the failure is due to a NotificationException or any other exception type, the ClientMessageListener#onError callback is triggered.

Conclusion

Lightstreamer seamlessly integrates real-time data streaming with robust client-to-server messaging, offering a comprehensive solution for modern, interactive applications. By providing three distinct messaging modes — In-Order Processing, Unordered Processing, and Fire-and-Forget — Lightstreamer enables developers to finely tune the balance between reliability and performance to suit specific needs. Whether it’s ensuring strict sequence integrity, prioritizing speed, or maximizing throughput with minimal overhead, each mode is designed to address a unique set of challenges. Coupled with the flexibility of the ClientMessageListener for tracking message outcomes, Lightstreamer empowers applications to deliver both continuous data updates and responsive, high-speed communication. This versatility is key for creating scalable, efficient, and user-centric systems in today’s dynamic digital landscape.

See also

NOTE: The links below point to the SDK versions available at the time of writing.

Demos

July 24, 2025
Originally published: July 24, 2025


9 min read

Table of Contents