prefecthSize
消费端预取消息数量,每次消费者获取到到消息数量小于等于prefecthSize,例如prefecthSize是10,每次消费者从broker拉取10条消息到本地内存。
在activemq中设置prefectSize如下,创建队列时配置。
Destination destination=session.createQueue("gqueue?consumer.prefetchSize=10");
通过设置prefecthSize,按照实际需求平衡预取消息数量,当然这样还达不到优化到目的,还需要配合optimizeAcknowledge(开启批量消息确认优化)来达到提高消息处理效率。
optimizeAcknowledge
optimizeAcknowledge到设置方式如下,消费者创建时配置。
ConnectionFactory connectionFactory= new ActiveMQConnectionFactory ("tcp://192.168.0.15:61616?jms.optimizeAcknowledge=true&jms.optimizeAcknowledgeTimeOut=10000");
优化回传默认阀值是0.65*prefecthSize,当pendingAck消息堆积到达阀值,消息会一次性批量进行ack。
optimizeAcknowledgeTimeOut表示最迟ack时间长度,到达时间限制没有达到阀值也会进行批量ack回传。
如何提高消息消费速率?
为了达到提高消费者消费消息速率,可以设置prefecthSize为一个比较大的值,这个值时消费者的消费能力之内的;并且消费者开启optimizeAcknowledge,进行批量回传优化,此时获取消息和ack都是批量操作,能够减少网络开销,从而提高消费者消息消费速率。
需要注意什么?
需要注意的是,prefecthSize的配置需要结合实际的消费者消费能力。假如配置过大,某个消费者一次性拉取的大量的消息,但是却无法及时完成处理,这会导致其他消费者干等待,而拉取大量消息的消费者却一直在忙活;假如配置过小,就达不到我们理想的优化效率,因此这个值怎么设置,需要实际生产中去调试。
下面进行简单例子来进行测试
设置prefecthSize为50的一个生产者,连续发送100条消息到broker,代码如下。
public class JMSCommonLoopProducer {
public static void main(String[] args) throws JMSException {
//根据broker URL建立连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.0.15:61616");
//创建连接
Connection connection = connectionFactory.createConnection();
connection.start();
//创建会话
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建队列(有则不创建)
Destination destination = session.createQueue("garine-queue?consumer.prefetchSize=10");
//创建生产者
MessageProducer producer = session.createProducer(destination);
for (int i =0;i< 100;i++){
//创建文本消息,有多种消息类型
TextMessage textMessage = session.createTextMessage("Hello garine" + "-" +i);
//发送消息
producer.send(textMessage);
}
System.out.println("over");
session.close();
}
}
然后是启动两个消费者,消费者同时开启了优化回传。
public class JMSSessionPrefecthConsumer {
public static void main(String[] args) throws JMSException, InterruptedException {
//根据broker URL建立连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.0.15:61616?jms.optimizeAcknowledge=true&jms.optimizeAcknowledgeTimeOut=100000");
//创建连接
Connection connection = connectionFactory.createConnection();
connection.start();
//创建会话
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建队列(有则不创建)
Destination destination = session.createQueue("garine-queue?consumer.prefetchSize=10");
//创建生产者
MessageConsumer consumer = session.createConsumer(destination);
//接受文本消息,阻塞等待
for (int i=1;i<=100;i++){
TextMessage textMessage = (TextMessage) consumer.receive();
System.out.println(textMessage.getText());
Thread.sleep(2000);
}
session.close();
}
}
两个消费者执行结果如下:
消费者1
消费者2
可以看出每次消费者获取消息都是批量获取的,例如消费者1第一次获取10条消息到本地内存(unconsumeMessage列表),那么就不会分派给消费者2。消费者2就从第11条开始获取消息,同样也是批量获取,交替获取。
同时还需要注意,这里还开起的优化回传,通过activemq控制台可以看到,每次Number Of Pending Messages被确认时都是7条一起确认的,0.65*10 = 6.5 取整数刚好七条一起回传。
试着把jms.optimizeAcknowledgeTimeOut=5000作为消费配置,发现每次确认消息变成3条,也就是说这个时间限制是起作用的,超过时间限制还达不到阀值的时候就会发起一次批量ack,这里面阀值是7,但是超过5000ms还没到达阀值,因此3条消息也要进行回传。