**优点:**及时性好
**缺点:**如果客户端没有做好流控,一旦服务端推送大量消息到客户端时,就会导致客户端消息堆积甚至崩溃。
主要关注点在订阅topic后,消费方式模式
@Test
public void pushDemo()throws Exception {
DefaultMQPushConsumer pushConsumer = new DefaultMQPushConsumer("test_push_consumer");
// 指定NameServer地址
pushConsumer.setNamesrvAddr(RocketMQConfig.NAME_SERVER_ADDR);
// 指定消费位置
pushConsumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
// 指定消费模式 默认为集群消费
pushConsumer.setMessageModel(MessageModel.CLUSTERING);
// 以topic的形式订阅消费
pushConsumer.subscribe(RocketMQConfig.TEST_TOPIC, "TagA");
// 主要关注对象—并发消费
pushConsumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
for (MessageExt msg : msgs) {
String strA = msg.getProperties().get("a");
System.out.println(String.format("body:%s,tag:%s,aValue:%s", new String(msg.getBody()), msg.getTags(), strA));
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
pushConsumer.start();
Thread.sleep(Integer.MAX_VALUE);
}
@Test
public void pushOrderDemo()throws Exception {
DefaultMQPushConsumer pushConsumer = new DefaultMQPushConsumer("test_push_consumer");
// 指定NameServer地址
pushConsumer.setNamesrvAddr(RocketMQConfig.NAME_SERVER_ADDR);
// 指定消费位置
pushConsumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
// 指定消费模式 默认为集群消费
pushConsumer.setMessageModel(MessageModel.CLUSTERING);
// 以topic的形式订阅消费
pushConsumer.subscribe(RocketMQConfig.TEST_TOPIC, "TagA");
// 主要关注对象—顺序消费
pushConsumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
for (MessageExt msg : msgs) {
String strA = msg.getProperties().get("a");
System.out.println(String.format("body:%s,tag:%s,aValue:%s", new String(msg.getBody()), msg.getTags(), strA));
}
return ConsumeOrderlyStatus.SUCCESS;
}
});
pushConsumer.start();
Thread.sleep(Integer.MAX_VALUE);
}
当消费状态返回消费未成功的时候,就会进行重新消费,并发消费和顺序消费的重试方式是不一样的
如何配置
最大重试次数:消息消费失败后,可被重复投递的最大次数。
consumer.setMaxReconsumeTimes(10);
重试间隔:消息消费失败后再次被投递给Consumer消费的间隔时间,只在顺序消费中起作用。
consumer.setSuspendCurrentQueueTimeMillis(5000);
并发消费消费失败后会将消费失败的消息重新投递回服务端,再等待服务端重新投递回来,在这期间会正常消费队列后面的消息
并发消费失败后并不是投递回原Topic,而是投递到一个特殊Topic,其命名为%RETRY%ConsumerGroupName,集群模式下并发消费每一个ConsumerGroup会对应一个特殊Topic,并会订阅该Topic。
并发消费的重试间隔不可以自定义配置,而是根据失败次数逐级递增
第几次重试 | 与上次重试的间隔时间 | 第几次重试 | 与上次重试的间隔时间 |
---|---|---|---|
1 | 10s | 9 | 7min |
2 | 30s | 10 | 8min |
3 | 1min | 11 | 9min |
4 | 2min | 12 | 10min |
5 | 3min | 13 | 20min |
6 | 4min | 14 | 30min |
7 | 5min | 15 | 1h |
8 | 6min | 16 | 2h |
顺序消费会在本地重试直到最大重试次数,并不会将消息投递到服务端,这样做的目的是为了防止消息的乱序。消息的重试间隔也只在顺序消费中起作用
**优点:**客户端可以依据自己的消费能力进行消费
**缺点:**拉取的频率需要用户自己控制,拉取频繁容易造成服务端和客户端的压力,拉取间隔长又容易造成消费不及时
与push一样,可以通过指定tag标签,或者使用sql92来消费消息
@Test
public void pullDemo() throws Exception{
DefaultLitePullConsumer pullConsumer = new DefaultLitePullConsumer("test_pull_consumer");
pullConsumer.setNamesrvAddr(RocketMQConfig.NAME_SERVER_ADDR);
pullConsumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
pullConsumer.subscribe(RocketMQConfig.TEST_TOPIC,"*");
// 指定每次拉取的条数
pullConsumer.setPullBatchSize(20);
pullConsumer.start();
while (true) {
// 拉取消息
List<MessageExt> messageExtList = pullConsumer.poll();
for (MessageExt messageExt : messageExtList) {
String strA = messageExt.getProperties().get("a");
System.out.println(String.format("body:%s,tag:%s,aValue:%s", new String(messageExt.getBody()), messageExt.getTags(), strA));
}
}
}
@Test
public void pullAssign() throws Exception{
DefaultLitePullConsumer pullConsumer = new DefaultLitePullConsumer("test_pull_consumer");
pullConsumer.setNamesrvAddr(RocketMQConfig.NAME_SERVER_ADDR);
pullConsumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
pullConsumer.setPullBatchSize(20);
pullConsumer.start();
// 获取所有的队列
Collection<MessageQueue> messageQueues = pullConsumer.fetchMessageQueues(RocketMQConfig.TEST_TOPIC);
// 这里可以筛选出要消费的队列...
// 消费所有队列
pullConsumer.assign(messageQueues);
while (true) {
// 拉取消息
List<MessageExt> messageExtList = pullConsumer.poll();
for (MessageExt messageExt : messageExtList) {
String strA = messageExt.getProperties().get("a");
System.out.println(String.format("body:%s,tag:%s,aValue:%s", new String(messageExt.getBody()), messageExt.getTags(), strA));
}
pullConsumer.commitSync();
}
}
订阅所有标签
consumer.subscribe("test", "*");
订阅单个标签
consumer.subscribe("test", "TagA");
订阅多个标签
consumer.subscribe("test", "TagA||TagB");
SQL92可以通过消息的属性和消息的标签进行过滤,消息的标签的key为TAGS
消息的属性设置
Message message = new Message("test", "TagA", "hello world".getBytes());
message.putUserProperty("a", "1");
订阅标签为TagA并且属性a的值为1的消息
String bySQL = "(TAGS is not null and TAGS = 'TagA') and (a is not null and a = 1)";
pushConsumer.subscribe(RocketMQConfig.TEST_TOPIC, MessageSelector.bySql(bySQL));
pushConsumer.setMessageModel(MessageModel.CLUSTERING);
// 配置消费位置
pullConsumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
// 如果是指定时间点开始消费,就需要设置时间点 ConsumeFromWhere.CONSUME_FROM_TIMESTAMP
// 时间点的模式 20231015161121 年月日时分秒
// pullConsumer.setConsumeTimestamp(UtilAll.timeMillisToHumanString3(System.currentTimeMillis() - 1800000L));