什么是prefetch
prefetch即在activemq中消费者预获取消息数量,重要的调优参数之一。当消费者存活时,broker将会批量push prefetchSize条消息给消费者,消费者也可以配合optimizeAcknowledge来批量确认它们。由于broker批量push消息给消费者,提高了网络传输效率,此值默认为1000。
通过上述,我们对broker消息转发机制的了解,可以知道,broker端将会根据consumer指定的prefetchSize来决定pendingBuffer的大小,prefetchSize越大,broker批量发送的消息就会越多,如果消费者消费速度较快,再配合optimizeAck,这将是相对完美的消息传送方案。
不过,prefetchSize也会带来一定的问题,在Queue中(Topic中没有效果),broker将使用“轮询”的方式来平衡多个消费者之间的消息传送数量。如果消费者消费速度较慢,而且prefetchSize较大,这将不利于消息量在多个消费者之间平衡。通常情况下,如果consumer数量较多,或者消费速度较慢,或者消息量较少时,我们应该设定prefetchSize为较小的值。
上面的内容摘抄:http://blog.csdn.net/asdfsadfasdfsa/article/details/53501723
其实,prefetchSize与optimizeACK策略关系密切,将在下一个介绍optimizeACK的小节中详细说明。
什么是optimizeACK
可优化的消息ACK策略,关系到是否批量确认消息的策略,这个是Consumer端最重要的调优参数之一。optimizeAcknowledge 表示是否开启“优化ACK选项”,当开启optimizeAck策略后,该参数的具体含义和消费端的处理如下:
(1)如果消费端处理消息的时间超过optimizeAcknowledgeTimeOut,消费端会向broker主动确认pending ack里面的消息,即等待确认的消息,这个与prefetch size有很大的关系,如果prefetch size为1,那么broker push到消费端的消息数会是1,那么在当前消息未完成处理的情况下,pending ack肯定是0,所以不会向broker确认消息。但是,假设prefetch size为5,broker推送到消费者的消息是5条,消费端在处理第3条消息的时候,耗时很长,导致5条消息的处理时候早早的超过了optimizeAcknowledgeTimeOut,此时,消费端将会把前面2条已经确认的消息,告诉broker,让broker从队列中删除消息,此时broker发现消费端还可以存放2个消息,又push两条消息到消费端。
(2)除了optimizeAcknowledgeTimeOut这个参数会使消费者触发批量确认消息,还有另一个指标,即,成功消费但待确认的消息数量超过 预取数量prefetchSize * 0.65 时,也会触发消费端自动确认那些待确认的消息。
验证
先了解一下activemq控制台
- Number Of Pending Messages : 是指broker等待发送到consumer的数量
- Messages Enqueued :进行broker的消息总数量
- Messages Dequeued :被consumer成功消费并且确认,已经从队列中删除的数量
- Active Consumers:可以观察存活的consumer详情
下面看下consumer详情
- Prefetch:consumer的预取参数
- Dispatched Queue :预取消息所存放的队列的大小,等待被consumer处理
- Dispatched : 被broker分发到consumer的总数量
- Dequeues :确认的消息数量
** 验证小技巧 **
(1)prefetchSize设为20,待producer发送200条消息到broker后,启动consumer,consumer每5秒处理一条消息,查看activemq的后台,看下consumer详情
(2)optimizeACK与optimizeAcknowledgeTimeOut、prefetchSize参数互调,观察后台结果。
请自行实验,结论已经在上面。
实验代码:https://git.oschina.net/thinwonton/activemq-showcase
sender
public class Sender {
public static void main(String[] args) throws JMSException, InterruptedException {
Connection connection = ActiveMQManager.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue(Utils.QUEUE_NAME);
MessageProducer producer = session.createProducer(queue);
while (true) {
SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
for (int i = 0; i < 25; i++) {
String date = dateFormat.format(new Date());
String message = String.format("msg[%s][%s]", i, date);
TextMessage toMessage = session.createTextMessage(message);
producer.send(toMessage);
System.out.println("消息发送成功:" + message);
}
Thread.sleep(30000); //10秒发送一批次
}
}
}
consumer
public class HighPrefetchSizeConsumer {
public static void main(String[] args) throws IOException {
try {
ActiveMQConnection activeMQConnection = (ActiveMQConnection)ActiveMQManager.createConnection();
ActiveMQPrefetchPolicy prefetchPolicy = new ActiveMQPrefetchPolicy();
prefetchPolicy.setQueuePrefetch(10);
activeMQConnection.setPrefetchPolicy(prefetchPolicy); //预取策略
activeMQConnection.setOptimizeAcknowledge(true); //可优化的ACK,延迟确认
activeMQConnection.setOptimizeAcknowledgeTimeOut(1000);
activeMQConnection.start();
Session session = activeMQConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue(Utils.QUEUE_NAME);
MessageConsumer consumer = session.createConsumer(queue);
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
try {
System.out.println("-------- ----- --------- ");
System.out.println("触发消息接收");
TextMessage textMessage = (TextMessage) message;
String text = textMessage.getText();
handleMsg(text);
System.out.println("成功处理消息 : " + text);
} catch (JMSException e) {
e.printStackTrace();
}
}
});
System.out.println("is OptimizeAck:" + activeMQConnection.isOptimizeAcknowledge());
System.out.println("OptimizeAck timeout:" + activeMQConnection.getOptimizeAcknowledgeTimeOut());
// 线程一直等待
System.in.read();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void handleMsg(String msg) {
System.out.println("准备处理消息 : " + msg);
try {
Thread.sleep(5000); //5秒处理一个
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}