Temporary queue support in Habari Client libraries

What is a temporary queue?

Temporary queues are destinations with a scope limited to the connection that created it, and are removed on the server side as soon as the connection is closed. They are typically used for synchronous messaging (request/reply communication model). Their main advantage is that no queue management to provide unique destination names is required, because their uniqueness is provided by the server, and queues are removed automatically after use.

Use cases for temporary queues

The article Designing Messaging Applications with Temporary Queues by Thakur Thribhuvan shows a typical use case. In his example, many clients send messages with healthcare system information to a static request queue destination, and pass the identity of their individual temporary queue in the ReplyTo header of the message. The recipient of the message processes incoming messages in the static request queue, and sends a reply to the temporary queue in the ReplyTo header. The originating client then consumes the reply from the temporary queue.

The author summarizes: “In this configuration, temporary destinations can be viewed as analogous to callbacks in synchronous processing, i.e., when the callee wants to reach out to the caller in a decoupled way, without the caller and callee having to know about each other in advance.

Code example

The unit test for temporary queues in the Habari Client for RabbitMQ 1.5 demonstrates the interaction between a ‘client’ and a ‘server’ using a static queue for the requests and temporary queues for the replies.

The server code creates a static request queue (ServerRequestQueue) and a consumer for the request messages (RequestConsumer). The server then starts receiving requests (shown later, we first need to see the client side).

// SERVER: start a consumer waiting for request messages
ServerRequestQueue := Session.CreateQueue('StaticRequestQueue');
RequestConsumer := Session.CreateConsumer(ServerRequestQueue);

The client creates a destination object for the static request queue (ClientRequestQueue) and a message producer for the requests (RequestProducer). The client also a creates the temporary queue object (ClientTempQueue) and a consumer for the server response (ResponseConsumer).

  // CLIENT: prepare a "normal" destination queue
  ClientRequestQueue := OutSession.CreateQueue('StaticRequestQueue');
  RequestProducer := OutSession.CreateProducer(ClientRequestQueue);
  // CLIENT: create a temporary queue for responses
  ClientTempQueue := OutSession.CreateTemporaryQueue;
  // create a consumer for response messages
  ResponseConsumer := OutSession.CreateConsumer(ClientTempQueue);

Now the client creates and sends a request (RequestMsg), which also specifies the temporary queue in the ReplyTo header:

  // CLIENT: send message and specify a reply queue
  RequestMsg := OutSession.CreateTextMessage('request');
  // pass the temp queue name in the message header
  RequestMsg.JMSReplyTo := ClientTempQueue;
  // send it
  RequestProducer.Send(RequestMsg);

The server runs a receive loop to consume requests from the static request queue:

  // SERVER: get the request
  RequestMsg := RequestConsumer.Receive(100);
  if RequestMsg <> nil then
  begin
    // ... process the request
  end

When the server completed the message processing, it sends the response back to the client:

  // SERVER create reply queue based on message reply-to and send the response
  ResponseQueue := Session.CreateQueue((RequestMsg.JMSReplyTo as IQueue).QueueName);
  ResponseProducer := Session.CreateProducer(ResponseQueue);
  ResponseMsg := Session.CreateTextMessage('response');
  ResponseProducer.Send(ResponseMsg);

Finally, the client receives the response message:

  // CLIENT: receive the ResponseMsg from the temporary queue
  ReceivedResponseMsg := ResponseConsumer.Receive(100);
  if ReceivedResponseMsg <> nil then
  begin
    // ... process the response
  end else begin
    // error: no response received
  end;

Load balancing

This solution uses the terms “client” and “server” only to make the request/response communication clear: the client initiates the communication with a request, and the server responds. The “server” however is not necessarily a singular instance (a Delphi application). As the requests sent by the clients are stored in a single message queue on the message broker, it is possible to launch many identical Delphi applications, running the same logic shown above, to handle extra workload when needed.

Advertisements