Message processing nodes and parsers must work in a multi-instance, multithreaded environment. There can be many node objects or parser objects each with many syntax elements, and there can be many threads executing methods on these objects. The message broker design ensures that a message object and any objects it owns are used only by the thread that receives and processes the message through the message flow.
An instance of a message flow processing node is shared and used by all the threads that service the message flow in which the node is defined. For parsers, an instance of a parser is used only by a single message flow thread.
A user-defined extension should adhere to this model, and should avoid the use of global data or resources that require semaphores to serialize access across threads. Such serialization can result in performance bottlenecks.
User-defined extension implementation functions must be reentrant, and any functions they invoke must also be reentrant. All user-defined extension utility functions are fully reentrant.
Although a user-defined extension can spawn additional threads if required, it is essential that the same thread returns control to the broker on completion of an implementation function. Failure to do this will compromise the integrity of the broker and will produce unpredictable results.
The following message flow example will help you understand some of the threading considerations you should be aware of when designing and implementing your own user-defined nodes. It considers the example of a user-defined input node.
A message flow can be configured to run on a set of threads. This is determined by the number of input nodes in the message flow, and the value of the additionalInstances property of the message flow. These two elements determine the maximum capacity of the thread pool the message flow can use. Therefore, if your message flow has particular processing requirements that dictate single threaded execution, you need to ensure that this is the case.
Allowing the broker to start additional copies of the message flow in separate threads using the additionalInstances property is the most efficient way of preventing the input queue from becoming a bottleneck. However, creating separate threads allows parallel processing of messages from the message queue, so you should only use this property when the order messages are processed is not important.
Threads are created as a result of input node construction and operation. A thread remains active or idle in the thread pool, and idle threads remain in the pool until they are dispatched by an input node, or the broker is shut down.
The figure below illustrates thread allocation in a message flow.
Thread allocation in a message flow
Initially, Thread 1 is demanded (A), and waits for messages. When a message arrives (B), Thread 1 propagates the message, and dispatches Thread 2. Thread 2 receives a message immediately (C), and propagates, and dispatches Thread 3, which waits for a message (D). Thread 1 completes (E), and returns to the thread pool. Thread 3 then receives a message (F), dispatches Thread 1, and propagates the message. Thread 1 now waits for a message to arrive on the queue (G).
It is worth noting the point marked H. At this instance in the message flow, Thread 1 acquires a message, but because all other threads are active at that time, it cannot dispatch. The message is propagated.
After this message has been propagated, Thread 2 completes (I), receives a new message from the queue, and propagates this new message. Thread 3 then completes (J), and returns to the pool. Thread 2 then also completes (K). Because it did not dispatch, when Thread 1 completes (L), it cannot return to the thread pool even though there are no messages on the queue, and instead waits for a message to arrive on the queue.
Notices |
Trademarks |
Downloads |
Library |
Support |
Feedback
![]() ![]() |
as01460_ |