## 12.1 排他性消息消费 者

### 12.1 排他性消息消费 者

When messages are dispatched from an ActiveMQ broker, they’ll always be in first in,first out order. But if you have more than one message consumer for a queue in yourapplication(s), you can’t guarantee that the order in which the messages were dispatchedwill be the same order in which your application will consume them. This isbecause you never have control over the scheduling of threads used to deliver themessages on the client side—even if all your message consumers are using the sameconnection. Ideally you’d only have one message consumer to ensure ordering ofmessages. But you may also need to support failover, to have another instance of yourqueue message consumer take over the processing of the queue messages if the firstconsumer fails. ActiveMQ can support having multiple message consumers on aqueue, but having only one of them receive the messages from that queue. We’ll discussthis concept in the following subsection.

ActiveMQ代理总是以先进先出的方式转发消息.但是,如果你程序的一个队列中有多个消息消费者,你就无法保证程序按照代理发送消息的顺序来处理消息.这是因为在客户端,即使你的消息消费者都使用同一个连接,你也无法控制用于调度消息发送的线程.理想情况是你只使用一个消费者以保证处理消息的顺序.但是,你可能还需要支持失效转移,支持在第一个消息消费者失效后,使用队列的另外一个消息消费者实例来接管第一个小消费者.ActiveMQ支持一个消息队列拥有多个消息消费者,但是仅有一个消费者会从代理接收消息.我们将在下面的小节中阐述这个概念.

#### 12.1.1 选择一个排他性消息消费

For applications where message order is important, or you need to ensure that therewill be only one message consumer for a queue, ActiveMQ offers a client-side optionto have only one active message consumer process messages. The ActiveMQ messagebroker will select one consumer on the queue to process messages. The benefit ofallowing the broker to make the choice is that if the consumer stops or fails, thenanother message consumer can be selected to be active, as depicted in figure 12.1.

If you mix  standard consumers and  exclusive consumers on  the same queue,  theActiveMQ message broker will still only deliver messages to one of the exclusiveconsumers. If all the  exclusive consumers become  inactive, and there’s   stilla standard message consumer, then consumption of  queue messages will return  tothe normal  mode  of   delivery—the messages   will  be  delivered  in  a round-robin  fashion between all the remaining active standard message consumers.

You can create an exclusive consumer using a destination option on the client, likein the following code

queue = new ActiveMQQueue("TEST.QUEUE?consumer.exclusive=true");
consumer = session.createConsumer(queue);

The ability to select a message consumer to be exclusive can be used for more thanguaranteeing that messages are consumed by only one active message consumer. Youcan use the exclusive consumer pattern to create a distributed lock, as we’ll demonstratein the next section.

#### 12.1.2 使用 排他性消息消费 者 提供分布式锁

Often you use messaging to broadcast data from an external resource, be that changesto records in a database, or comma-separated values appended to a file, or a raw realtimedata feed. You might wish to build in redundancy, so if an instance of the applicationreading and broadcasting the changing data fails, another can take over. Oftenyou can rely on locking a resource (row lock or file lock) to ensure that only one processwill be accessing the data and broadcasting over a topic at a time. But when youdon’t want the overhead of using a database, or want to run processes across more thanone machine (and can’t use distributed locks), then you can use the exclusive consumerfunctionality to create a distributed lock. In figure 12.2 we show an applicationwhere we want failover for a client reading data from a real-time feed. We only wantone client to connect to the feed and distribute the events, but if it fails, we needanother client available to take over.

In order to use exclusive consumers to create a distributed lock, we need our messageproducer to subscribe exclusively to a well-known queue. If the message producerreceive a message from the queue, it becomes activated, and can then subscribe to thereal-time feed and transform the real-time data into JMS messages. Here’s a code snippetfor the message producer to initiate a distributed lock:

public void start() throws JMSException
{
this.connection = this.factory.createConnection();
this.connection.start();
this.session = this.connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
Destination destination = this.session.createQueue(this.queueName + "?consumer.exclusive=true");
Message message = this.session.createMessage();
MessageProducer producer = this.session.createProducer(destination);
producer.send(message);
MessageConsumer consumer = this.session.createConsumer(destination);
consumer.setMessageListener(this);
}

In this example, we always send a message to the well-known queue, to start  offconsumption— this step could always be done externally by a management process.Note  that  we  use  Session.CLIENT_ACKNOWLEDGE  mode  to  consume  the message.

Although we  want to  be notified  that we’re  an exclusive consumer—and hencehave the lock— we don’t want to remove the message from the well-known  queue.In this way, if we fail, another exclusive producer will be activated.

For this example, we’d implement the MessageListener to look like the followingcode snippet. If we’re not already activated, we call a fictional method—start-Producing(). If this were a real application, this method would start subscribing to areal-time feed and convert real-time data into JMS messages:

public void onMessage(Message message)
{
if (message != null && this.active==false)
{
this.active=true;
startProducing();
}
}

(译注:这个程序的应用场景: 利用排他性消息消费者,实现分布式锁. 当前的这个类首先是一个消息消费者(并且是排他性的消费者),从一个数据源接收消息.因为是排他性的,所以可以在多个机器上运行这个类,但同一时刻只有一个是处于激活状态的.处于激活状态时,当这个消费者接收消息时,调用onMessage方法,开始startProducing();如果当前的消费着失败了,则其他的消费者接替它继续工作,再次调用onMessage()方法,再次调用startProducing();.这样就相当于使用排他性消息消费者的特性,构造了一个可以分布式运行的producer.)

We’ve shown that using an exclusive consumer allows us to ensure that only one messageconsumer will be active at a time. In the next section, we’ll look at messagegroups, where the ActiveMQ broker can selectively choose a message consumer for allmessages that have the same JMSXGroupID message header property set.

微信赞赏  支付宝赞赏