JBoss.orgCommunity Documentation

Chapter 8. Messaging

8.1. Introduction
8.2. Deploying Destinations
8.2.1. Deployment Styles
8.2.2. Deployment Descriptors
8.3. TorqueBox Ruby Classes
8.4. Messaging Abstractions
8.4.1. Queues and Topics
8.4.2. Message Processors
8.4.3. Tasks
8.4.4. Backgroundable Methods
HornetQ

TorqueBox integrates the JBoss HornetQ message broker technology. It is automatically available to you, with no additional configuration required to start the messaging service. HornetQ supports clustered messaging, to allow for load-balancing, failover, and other advanced deployments.

The term "messaging" encompasses a large area of functionality. Messaging solutions are used to achieve loosely-coupled, asynchronous systems. The primary actors in a messaging-based system are messages, destinations, consumers, and producers. From an implementation perspective, a broker mediates the relationships between the other actors.

Messages

The unit of communication within a messaging system is a message. A message may either be simply a blob of octets, or it might have some higher-order, application-defined semantics. All messages include a set of headers, similar to email.

Destinations

A destination represents a rendezvous where messages are exchanged. A message may be sent to a destination by one actor, and received from the destination by another.

There are two main types of destinations: queues and topics. All destinations allow multiple actors to place messages with them. The type of destination affects what happens to the message once given to the destination. A queue delivers the message to a single recipient (possibly one of many candidate recipients). A topic delivers the message to multiple interested recipients.

In the image below, the green lines represent the flow of a single message from a producer to one-or-more consumers through a topic and a queue.

Producers

Any component or client code that creates messages and gives them to the message broker for delivery is considered a producer. Generally speaking, the producer does not know the details of the destination.

Consumers

Any component that waits for messages to be delivered to it by the message broker is consider a consumer. A consumer is unaware of the producer and any other consumers, potentially.

Queues and topics (collectively known as destinations) may be deployed with your application, or separate from your application. Additionally, various parts of your application may also implicitly deploy and use some destinations.

Each method has advantages and disadvantages involving the expectations of your application and its interaction with resources outside the scope of the application.

You have several options when deploying queues and topics, based on the lifecycle that suits your systems best.

If your queues and topics have a lifecycle that extends beyond the deployment of any single app, you may want long-lived queues and topics. Long-lived destinations are first-order components, and may be deployed on their own. In this way, many applications can come and go over time, publishing and consuming from the same queues.

When using long-lived destinations, the corresponding deployment descriptors are placed directly into the apps/ directory of TorqueBox AS.

Queues

To deploy queues, a simple YAML file is required, simply naming the queue, and providing additional configuration parameters. The file should either be named exactly queues.yml or have the suffix of -queues.yml, such as these-queues.yml or those-queues.yml. The only configuration option available on queues is the durable option.

Durability is enabled by default, and involves writing each message to disk so as to be able to recover in the event of failure or server restart. Disabling durability on queues may result in better performance, but increases the risk of losing messages.


Topics

To deploy topics, a simple YAML file is required, simply naming the topic, and providing additional configuration parameters. The file should either be named exactly topics.yml or have the suffix of -topics.yml, such as these-topics.yml or those-topics.yml. Currently, no additional configuration parameters are allowed - topic durability is controlled via options on the attached processors (See Section 8.4.2.3.1, “Connecting consumers within TorqueBox”).


All classes in the TorqueBox::Messaging module reside in the Ruby gem, torquebox-messaging, so to use them in your Rails app, you'll need to configure your app to load the gem.

Rails 2.x

Add this to your config/environment.rb:


Rails 3.x

Add this to your Gemfile:


And to use them in any other JRuby script, it's even simpler. First, ensure that rubygems is loaded, then require the torquebox-messaging feature.


There are two main messaging destination abstractions: TorqueBox::Messaging::Queue and TorqueBox::Messaging::Topic. Each has a publish and a receive method, and each must be constructed with a name and an optional hash of options:


Though sometimes convenient, these methods are fairly low-level and higher-level abstractions such as MessageProcessors, Tasks, and Backgroundable are often better-suited to the task.

It's trivial to publish a message to a JMS Queue or Topic with TorqueBox. And if all of your message consumers are Ruby clients, the contents of the messages can be any serializable Ruby or Java object. You just need to ensure that the type of content you produce resides in the runtime environments of both the producer and the consumer.

To send a message, you will need access to a Topic or Queue instance. The preferred method for accessing the destination instance is to use inject(...) (see Messaging Destinations for more details). If you need to pass options to the instance, or only have access to the destination name at runtime, construct either a Topic or a Queue instance with its name and options. Once you have a destination instance, simply call its publish method. The API's of both Topics and Queues are identical; they each simply represent a destination for your messages.




The publish method takes an optional second argument containing a hash of options:

Table 8.2. Publish options

OptionDefaultDescription
:priority:normalhigher priority messages will be delivered before lower priority messages within the context of a queue. You can specify the priority as an integer in the range 0..9, or as one of the following convenience symbols (with the corresponding integer priorities in parentheses):
  • :low (1)

  • :normal (4)

  • :high (7)

  • :critical (9)

Higher priority messages will be processed before lower priority ones for a specific message processor.
:ttl The maximum time the message will wait in a destination to be consumed, in milliseconds. If the message isn't consumed within this time it will be delivered to an expiry queue. By default, messages don't have a ttl (and therefore never expire). By default, expired messages end up on the /queue/ExpiryQueue queue. If you want to do something special with those messages, you'll need to add a processor for that queue.
:persistenttrueBy default, queued messages will survive across AS restarts. If you don't want a message to be persistent, set the persistence to false (see Section 8.2.2.1, “Long-lived queues and topics for controlling message durability globally for a queue).
:correlation_idnilThe string value to set for the JMSCorrelationID message header.
:reply_tonilThe javax.jms.Destination value to set for the JMSReplyTo message header.
:typenilThe string value to set for the JMSType message header.
:propertiesnilA hash of string key/value pairs to set as message properties. This can be used as application-specific headers and matched against in the :selector option of the receive method.
:startup_timeout30000The maximum time to wait for the destination to become ready on initial app startup, in milliseconds. On a very slow machine this may need to be increased from the default.

Message consumers may be implemented in Ruby and easily attached to destinations. A Ruby consumer may either interact at the lowest JMS-level, or take advantage of higher-level semantics.

Message consumers may extend TorqueBox::Messaging::MessageProcessor and implement an on_message(body) method which will receive the body of the JMS message.

Example 8.13. MessageProcessor subclass

class MyConsumer < TorqueBox::Messaging::MessageProcessor
  def on_message(body)
    # The body will be of whatever type was published by the Producer
    # the entire JMS message is available as a member variable called message()
  end
  def on_error(exception)
    # You may optionally override this to interrogate the exception. If you do, 
    # you're responsible for re-raising it to force a retry.
  end
end

There is an accessor for the actual JMS message that is set by TorqueBox prior to invoking on_message, so it's there if you need it.


Just like with process!, if on_message raises an exception, the message broker considers the message undelivered. You may trap the error by overriding on_error, at which point you decide whether to re-raise the exception to force a retry. That is the default behavior if you do not override the method.

You can connect consumers hosted within a TorqueBox-based application, or in external scripts. The method for each environment is similar, but slightly different, since TorqueBox-hosted consumers get more baked-in support from the container.

To connect consumers within a TorqueBox-deployed application, you need to add a messaging: section to your torquebox.yml (or external *-knob.yml descriptor).

This section will contain the mappings from your destinations (topics and queues) to your consumers. The section is a YAML hash, the keys of which are your destination names, which should correspond to existing queues and topics. These destinations may be deployed through the same torquebox.yml or as long-lived destinations.


The above example shows the simplest possible configuration, but it's possible to alter the behavior of your message processor using the following options:



Actually, YAML enables the configuration to get fairly sophisticated, allowing you to, for example, map a single destination to multiple processors or re-use configuration options in multiple processors. You may never have a need for this much flexibility, but it's available if you do.


A special case of message queues is to execute some task asynchronously, perhaps even remotely. Rails developers often want to spawn a potentially long-running task in response to a user request, without forcing the user to wait for its completion. TorqueBox makes this easy.

When invoking a task, you can override the default priority, time-to-live, and persistence options by passing an options hash to async. The available options are:


The options are passed as the third argument to async:


In addition to task classes for background proccessing, TorqueBox also provides Backgroundable methods. Backgroundable allows you to process any method on any object asynchronously. You can mark a method to always execute in the background, or send a method to the background on an ad hoc basis.