(Quick Reference)
2 Configuration - Reference Documentation
Authors: Jeff Brown, Peter Ledbrook
Version: 1.0.0
2 Configuration
The plugin supports a number of configuration options which all may be expressed in
grails-app/conf/Config.groovy
. A basic configuration might look something like this:
// grails-app/conf/Config.groovy
rabbitmq {
connectionfactory {
username = 'guest'
password = 'guest'
hostname = 'localhost'
}
}
Those are settings which are necessary in order for the plugin to be able to connect to and communicate with a RabbitMQ server.
Following is a list of other configuration settings supported by the plugin.
Configuration Property | Description | Default |
---|
rabbitmq.connectionfactory.username | The user name for connection to the server | (none) |
rabbitmq.connectionfactory.password | The password for connection to the server | (none) |
rabbitmq.connectionfactory.hostname | The host name of the server | (none) |
rabbitmq.connectionfactory.virtualHost | The name of the virtual host to connect to | '/' |
rabbitmq.connectionfactory.channelCacheSize | The connection channel cache size | 10 |
rabbitmq.concurrentConsumers | The number of concurrent consumers to create per message handler. Raising the number is recommended in order to scale the consumption of messages coming in from a queue. Note that ordering guarantees are lost when multiple consumers are registered. | 1 |
rabbitmq.disableListening | Disables all service listeners so that they won't receive any messages. | false |
rabbitmq.retryPolicy.maxAttempts | Sets the maximum number of retries for failed message deliveries | 0 |
2.1 Configuring Queues
Queues must be declared in the RabbitMQ server before consumers can be associated with those queues and before messages may be sent to those queues. If the Grails application will be sending messages to or receiving messages from queues that may not already be declared in the RabbitMQ server, the application needs to declare those queues up front. One way to do that is to add beans to the Spring application context of type
org.springframework.amqp.core.Queue
. That
might look something like this:
// grails-app/conf/spring/resources.groovy
beans = {
myQueue(org.springframework.amqp.core.Queue, 'myQueueName')
myOtherQueue(org.springframework.amqp.core.Queue, 'myOtherQueueName') {
autoDelete = false
durable = true
exclusive = false
arguments = [arg1: 'val1', arg2: 'val2']
}
}
The plugin also supports a DSL for describing these queues. This DSL is expressed in
grails-app/conf/Config.groovy
. The code below configures the same queues as the previous code sample.
// grails-app/conf/Config.groovy
rabbitmq {
connectionfactory {
username = 'guest'
password = 'guest'
hostname = 'localhost'
}
queues = {
myQueueName()
myOtherQueueName autoDelete: false, durable: true, exclusive: false, arguments: [arg1: 'val1', arg2: 'val2']
}
}
With both techniques, the autoDelete
, durable
and exclusive
attributes default to false
and the arguments
attribute defaults to null.
So what do those queue options mean?
Option | Description |
---|
autoDelete | If true , the queue will be removed from the broker when there are no more clients attached to it. Note that this doesn't take effect until after at least one client connects to the queue. |
durable | If true , the queue will survive a restart of the broker. |
exclusive | Only the client that created the queue can connect to it. |
One final thing: when you declare a standalone queue like this, it automatically gets bound to the broker's default exchange, which has an implicit name of '', i.e. the empty string. You can easily send messages to this queue via the
rabbitSend method.
2.2 Configuring Exchanges
Queues are the foundation of consuming messages, but what if you want to send messages? In AMQP, you send messages to an exchange and the exchange then routes those messages to the appropriate queues based on something called a binding. The key to setting up complex messaging systems lies in configuring these exchanges and queues appropriately.
Declaring an exchange
Let's start with an example of how to set up a simple exchange (with no queues):
rabbitmq {
connectionFactory {
…
}
queues = {
exchange name: 'my.topic', type: topic
}
}
As you can probably guess, this will create a topic exchange with the name 'my.topic'. There are two things to note at this point:
- the name and type are required
- the type value is not a string literal
So what types are available to you?
Type | Description |
---|
direct | An exchange that only routes messages that are bound to it with a key that matches the routing key of the message exactly. Typically this exchange is used for point-to-point messaging and the routing key is the queue name. |
fanout | Sends messages to all queues bound to it. It basically does a broadcast. |
topic | Similar to the fanout exchange, this routes messages to the queues bound to it, but only queues whose binding matches a message's routing key will receive that message. Wildcards are supported in the binding. |
headers | Similar to topic, but messages can be filtered by other any message header, not just the routing key. |
The exchange declaration also supports a couple of extra options that should be familiar from the queue declarations:
Option | Description |
---|
autoDelete | If true , the exchange will be removed from the broker when there are no more queues bound to it. Note that this doesn't take effect until at least one queue is bound to the exchange. |
durable | If true , the exchange will survive a restart of the broker. |
With the above syntax, it is up to you to bind queues to the exchange via another AMQP client or via the RabbitMQ management interface. In other words, this is most suitable if your Grails application is purely a publisher of messages and not a consumer (or at least not a consumer of 'my.topic' messages).
What if you want to create queues and automatically bind them to the exchange? Don't worry, that's supported by the configuration DSL too.
Binding queues to exchanges
An exchange on its own isn't particularly useful, but we can easily bind queues to it by declaring them as nested entries:
rabbitmq {
connectionFactory {
…
}
queues = {
exchange name: 'my.topic', type: topic, durable: false, {
foo durable: true, binding: 'shares.#'
bar durable: false, autoDelete: true, binding: 'shares.nyse.?'
}
}
}
In the example above, we bind two queues ('foo' and 'bar') to the exchange 'my.topic'. Since this is a topic exchange, we can use a binding key to filter which messages go from 'my.topic' to each queue. So in this case, only messages with a routing key beginning with 'shares.' will end up on the 'foo' queue. 'bar' will only receive messages whose routing key begins with 'shares.nyse.'.
This approach isn't limited to topic exchanges: you can automatically bind queues to any exchange type. There are a few things to bear in mind though:
- the default binding for direct exchanges is the queue name (unless this is explicitly overridden by a 'binding' option);
- the 'binding' is ignored for fanout exchanges; and
- the headers exchange requires a map of message header names and values for its binding.
RabbitMQ has several built-in exchanges with names of the form 'amq.*', for example 'amq.direct'. If you want to bind to these, you currently have to declare them with the correct attributes, i.e.exchange name: "amq.direct", type: direct, durable: true, autoDelete: false
As you can imagine, these few building blocks allow you to configure some pretty complex messaging systems with very little effort. You can tailor the messaging system to your needs rather than tailor your applications to the messaging system.
2.3 Advanced Configuration
When you need fine-grained control over your service listeners, you can tap into the power of Spring. Since each service listener is implemented as a set of Spring beans, you can use Grails'
bean property override mechanism to provide your own low-level settings.
So how are these beans set up? If a service has either a
rabbitQueue
or
rabbitSubscribe
property, then you will have these beans:
As an example, let's say you have a
MessageStoreService
like so:
class MessageStoreService {
static rabbitSubscribe = [exchange: "amq.topic", routingKey: "logs.#"]
…
}
You can then customise things like the number of concurrent consumers, whether the channel is transacted, what the prefetch count should be, and more! Simply add code like this to your runtime configuration (Config.groovy):
beans {
messageStoreService_MessageListenerContainer {
channelTransacted = false
concurrentConsumers = 10
prefetchCount = 5
queueNames = ["q1", "q2"] as String[]
} messageStoreServiceRabbitAdapter {
encoding = "UTF-8"
responseRoutingKey = "replyQueue"
}
}
This approach works for any property that accepts a basic type. But what about bean references? In this case, you can't use the bean property overrides. Fortunately, the most common bean reference you are likely to want to override, the message converter, has a dedicated configuration option:
rabbitmq.messageConverterBean = "myCustomMessageConverter"
This is a global setting that accepts the name of a message converter bean. For the rare occasions that you need to override other bean references, you can declare your own
<serviceName>_MessageListenerContainer
or
<serviceName>_RabbitAdapter
beans in resources.groovy.
Finally, you can override some of the global config options on a per-service basis:
rabbitmq {
services {
messageStoreService {
concurrentConsumers = 50
disableListening = true
}
}
}
There are many options for customisation and we hope the above will get you started.