Threading

The Solace JMS API uses a processing context to organize communications between a client application and an event broker. This context encapsulates threads that drive network I/O and message delivery notification for the connections between a client application and an event broker. Message delivery and reception also require application-provided threads.

By default, a processing context is used for each JMS connection that is created. However, to use a single context for all JMS connections made by the JMS client, you can set the InitialContext environment property SOLACE_JMS_USE_DEFAULT_CONTEXT property to true. Each context requires additional threads.

Threading When Receiving Messages

When receiving messages, the processing context uses a context thread to read messages off the socket and parse them. For an asynchronous MessageConsumer, a second context thread is typically used to perform consumer notification and message dispatch. (However, when using ultra low-latency applications, the same single context thread can also be used for an asynchronous MessageConsumer.) For a synchronous MessageConsumer, the same single context thread can enqueue messages for delivery or consumption.

Receiving Message Asynchronously

A client application can receive messages in an asynchronous manner through a MessageListener. That is, when messages are available, they are automatically sent (“pushed”) to a message listener interface provided by the client application.

When receiving messages asynchronously, the processing context uses a thread for consumer notification and dispatching the enqueued messages to consumers; all listeners (MessageListener and ExceptionListener), also run from a processing thread.

By default, the context uses one thread to read messages off the network, parse them, and then enqueue them on the consumer notification dispatcher queue. It uses another thread for consumer notification and dispatching the queued messages to consuming client applications.

The maximum number of messages that can be queued by the context per Session before they are delivered to consumers is as follows:

  • Direct messages (that is, Non-Persistent messages sent using Direct Transport)—5,000
  • Guaranteed messages (that is, Non-Persistent and Persistent messages sent using Guaranteed Transport)—The maximum number of Guaranteed messages permitted by each consumer’s sliding Guaranteed Message window size. A Guaranteed Message window size limits the number of messages that a consumer can receive before an acknowledgment must be returned to the event broker that it received the messages in the window.

    The Guaranteed Message window size is set through the receive‑ad‑window-size message delivery property of the Connection Factory. The value set for this property is applied to each consumer that uses the Connection Factory.

The consumer notification dispatcher queue that is used to asynchronously notify consumers of messages, through a MessageListener and of exceptions, through an ExceptionListener, can be resized, if necessary. This queue should be large enough to buffer the maximum number of notifications that are generated for all consumers (of both Guaranteed and Direct messages) in all of the Sessions within a JMS connection. If the MessageListener or ExceptionListener do not always return control quickly and the consumer notification dispatcher queue fills up, the API thread attempting to enqueue notifications to this queue can be temporarily blocked, which can cause messages to queue on the event broker.

The consumer notification dispatcher queue size can be modified through the SOLACE_JMS_CONSUMER_DISPATCHER_QUEUE_SIZE property. However, this is not typically required.

The figure below shows the context threads that are used to receive messages asynchronously.

Receiving Messages Asynchronously

Receiving Async

Receiving Messages Synchronously

When receiving messages in a synchronous manner, the client application uses explicit receive calls to retrieve messages from the message queues for each consumer. When receiving messages synchronously, the client application provides the threads that “pull” the enqueued messages from the API.

To receive messages, the client application must use Connection.start() to enable receiving messages from the event broker and then use synchronous MessageConsumer.receive(...) calls to receive the next available message. The receive methods can manage the potential blocks by waiting indefinitely until there are messages, not waiting when there are no messages (that is, immediately timing out when there are no messages), or timing out after a set period of time when there are no messages.

The figure below shows the context thread and application threads that are used when messages are received synchronously through the API.

Receiving Messages Synchronously

Receiving Sync