3.5. Receivers and Correlators

Receivers and Correlators are two optional components of a performance test scenario that enables us to receive response from a separate message channel. We can for instance send a request through a REST API and await for the response in the file system or in a database.

A Receiver has three mandatory properties. These are the number of concurrent threads that should be created to receive responses, the correlator to be used to match responses to requests, and the source from where to receive the responses. An instance of Correlator is also passed to the Receiver for it to be able to register the response.

It is a crucial responsibility of the Receiver to spawn the threads and establish communication channels. A Receiver must start the defined number threads to receive messages. These threads are later stopped with Thread.interrupt(). It is up to the receiver threads to react accordingly. The receiver threads must be executed as daemon threads and can be terminated at the end of the test execution if they do not react to the interruption.

During a performance test execution there is just a single instance of a Receiver created. For the developers convenience there is AbstractReceiver. For the most common situations where the Receiver simply creates a thread pool, there is AbstractAutoSpawnReceiver. A developer just needs to extends the run() method then. On the other hand, HttpReceiver is an example of a Receiver where the underlying framework did not allow us to spawn the threads ourself and manages the pool itself, thus is extends AbstractReceiver.

All received messages are passed to a Correlator which notifies the correct SenderTask. The Correlator interface defines two methods, first for the outbound requests (registerRequest()), and second for the inbound responses (registerResponse()). The Correlator then correlates requests with their responses and notifies SenderTask of receiving the appropriate response to the original request. This is done based on a correlation ID that is extracted from both request and response. Upon a successful match, SenderTask.registerResponse(Serializable) is called.

For performance reasons, all interface methods should be implemented thread safe without locking and or synchronization. All implementations should make sure that they do not keep eating up the memory and clean their data structures regularly.

For the developers convenience, there is AbstractCorrelator that takes care about matching the messages together. All one needs to implement are methods to extract the correlation ID from both the request (getRequestCorrelationId()) and the response (getResponseCorrelationIds()).

Please note the difference that a response can actually contain multiple IDs. The method for processing responses also takes a MultiMap instance with the message headers. This is because some protocols allow the same message header to be present multiple times (e.g. HTTP).

The Correlator does not necessarily need to have just a passive role. It can even add the correlation ID in the request if there is none yet. See GenerateHeaderCorrelator that can be used for many protocols.

It is possible that multiple requests will be aggregated in a single response. It is a task of a Correlator implementation to report all the aggregated responses. It is also possible that a single request will trigger multiple responses. Again, a specialized Correlator implementation can handle this situation and wait for all the parts to be received.