前言
从本文中你可以了解到如下内容:
1) consumer端消息消费的模型,session的运作机制
2) 如果提升broker和consumer端消息消费的速率
3) selector,group,exclusive对消息消费的影响
4) 如何让Priority更好的运行,提高消息的顺序性
5) Slow Consumer的产生原因,以及如何调优。
Consumer作为ActiveMQ的消费端,开起来简单,不过还有很多隐藏的特性,值得我们去探索和调优。
如下为典型的Consumer端代码示例
Java代码 String brokerUrl = "tcp://localhost:61616?"
String queueName = "test-queue";
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(brokerUrl);
ActiveMQConnection connection = (ActiveMQConnection)factory.createConnection();
final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination queue = session.createQueue(queueName);
MessageConsumer consumer = session.createConsumer(queue);
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
try{
ActiveMQMessage m = (ActiveMQMessage)message;
System.out.println(m.toString());
}catch(Exception e){
//
e.printStackTrace();
}
}
});
connection.start();
//connection.close();
Consumer端涉及到众多的特性,其中包括:消息转发机制,session机制与多线程,消息ACK策略等,我们无法详解所有的原理,如下为Consumer端消息转发机制,仅供参考,[详细请参考]:
上图非常复杂,具体的原理,我们将会在下文中逐个解释。大概原理与过程:
通过Connection实例创建Session之后,将会把session实例保存在本地的list中,即connection持有session列表,并在底层开启transport,侦听数据。
Session创建Consumer之后,将会把Consumer实例添加到本地的list中,以便此后分拣消息,即session持有consumer列表;此外connection中也持有一个consumer集合(Map),其中Key为consumerId,value为session引用。
如果Session支持异步转发(asyncDispatch)或者使用了转发池(dispatchPool),将创建线程池用来转发消息。
当broker端有消息通过transport发送时,connection将会分拣消息,根据消息中指定的consumerId,从本地session列表中获取其对应的session实例;然后将消息交付session负责转发。
当session接受到消息后,会根据消息的consumerId,在本地consumer列表中找到对应的consumer实例;检测session的消息转发方式,如果是同步转发,则直接将消息交给consumer,即调用consumer.dispatch(message),此方法要么调用messageListener.onMessage(),要么将消息添加到本地的unconsumedMessages队列中(唤醒receive);如果是异步转发,则将消息添加到session级别的队列中并由线程池负责转发。
当consumer接受到消息之后,将会调用messageListener.onMessage方法或者从receive方法中返回。
当消息消费成功后,consumer将会根据指定的ACK_MODE负责向broker发送ACK指令,此后消息将会在broker端清除。
1. asyncDispatch
broker端是否允许使用“异步转发”。broker端处理connection、session、consumer的模型和Client端几乎一样;当通道中(Queue/Topic)有消息需要转发给consumer时,将会调用相应的Subscription的dispatch方法(在broker端负责与Consumer通讯的服务,称为Subscription,每个Consumer客户端对应broker端一个Subscription实例);如果asyncDispatch为false,那么将会阻塞转发线程(dispatchThread),直到底层的Transport将消息发送到Consumer客户端(如果底层transport正在发送消息,意味着阻塞);如果为true,则会将消息添加到transport本地的队列中(负责与Consumer客户端通信的每个transport都有一个本地的queue和线程池),并启动线程池负责发送消息,那么转发线程即可立即返回。
(参见代码:QueueStoreCursor/TopicStoreCursor --> broker端Queue/Topic(核心类)--->QueuePrefetchSubscription,QueueRegion,AbstractRegion,RoundRobinDispatchPolicy/StrictOrderDispatchPolicy)
由此可见,当前通道中所有的consumer都在brorker端开启了“异步转发”,可以大大提升broker转发消息的性能,此值默认为true。
Java代码 //在brokerUrl中设置
tcp://localhost:61616?jms.dispatchAsync=true
//在destinationUri中为当前通道设定
orderQueue?consumer.dispatchAsync=true
broker“异步转发”采用的是线程池模式,线程池的缺点即使当任务单元运行时间足够短时,线程切换的成本将变得很重要,这也意味着