Producer - 加入了id为key,msg为bean的json字符
public class AddProducer {
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("a-group");
producer.setNamesrvAddr("192.168.0.211:9876");
producer.start();
Alarm alarm1 = new Alarm(1, "add", new SimpleDateFormat("yyyy-MM-dd").parse("2024-01-01"));
Alarm alarm2 = new Alarm(2, "add", new SimpleDateFormat("yyyy-MM-dd").parse("2024-01-02"));
Alarm alarm3 = new Alarm(3, "add", new SimpleDateFormat("yyyy-MM-dd").parse("2024-01-03"));
Alarm alarm4 = new Alarm(4, "add", new SimpleDateFormat("yyyy-MM-dd").parse("2024-01-04"));
Alarm alarm5 = new Alarm(5, "add", new SimpleDateFormat("yyyy-MM-dd").parse("2024-01-05"));
Alarm alarm6 = new Alarm(6, "add", new SimpleDateFormat("yyyy-MM-dd").parse("2024-01-06"));
Alarm alarm7 = new Alarm(7, "add", new SimpleDateFormat("yyyy-MM-dd").parse("2024-01-07"));
Alarm alarm8 = new Alarm(8, "add", new SimpleDateFormat("yyyy-MM-dd").parse("2024-01-08"));
Alarm alarm9 = new Alarm(9, "add", new SimpleDateFormat("yyyy-MM-dd").parse("2024-01-09"));
Alarm alarm10 = new Alarm(10, "add", new SimpleDateFormat("yyyy-MM-dd").parse("2024-01-10"));
ArrayList<Alarm> list = new ArrayList<>();
list.add(alarm1);
list.add(alarm2);
list.add(alarm3);
list.add(alarm4);
list.add(alarm5);
list.add(alarm6);
list.add(alarm7);
list.add(alarm8);
list.add(alarm9);
list.add(alarm10);
try {
for (Alarm alarm : list) {
Message msg = new Message("ALARM_RECORD", "add",String.valueOf(alarm.getId()), JSONUtil.toJsonStr(alarm).getBytes(RemotingHelper.DEFAULT_CHARSET));
producer.send(msg,new CustomMessageQueueSelector(),alarm.getId(),new CustomSendCallback());
System.out.println(alarm.getId() + " Continue execution ");
}
} catch (Exception e) {
e.printStackTrace();
}
// 6. 关闭生产者
// producer.shutdown();
}
}
Selector
public class CustomMessageQueueSelector implements MessageQueueSelector {
@Override
public MessageQueue select(List<MessageQueue> list, Message message, Object o) {
Integer id = (Integer) o;
long index = id % list.size();
return list.get((int) index);
}
}
那么都知道 id 1和id9 用的是queue1
Consumer
public class ConsumerAdd {
public static void main(String[] args) throws Exception {
// 实例化消费者
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("record_add_group");
// 指定Namesrv地址
consumer.setNamesrvAddr("192.168.0.211:9876");
// 订阅主题和标签
consumer.subscribe("ALARM_RECORD", "add");
// 设置Consumer第一次启动是从哪个位置开始消费
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
// 注册消息监听器
consumer.registerMessageListener(new CustomMessageListenerOrderly());
// 启动消费者
consumer.start();
System.out.printf("Consumer tag add Started.%n");
}
}
监听器
@Slf4j
public class CustomMessageListenerOrderly implements MessageListenerOrderly {
/**
* 保持消息消费的次数
*/
Map<String, Integer> map = new HashMap<>();
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, ConsumeOrderlyContext consumeOrderlyContext) {
try {
for (MessageExt msg : list) {
String data = new String(msg.getBody());
Alarm alarm = JSONUtil.toBean(data, Alarm.class);
if(alarm.getId() == 1){
throw new Exception();
}
System.out.println("正常消费:" + alarm);
}
return ConsumeOrderlyStatus.SUCCESS;
} catch (Exception e) {
MessageExt msg = list.get(0);
map.put(msg.getKeys(),msg.getReconsumeTimes()+1);
log.info(msg.getKeys()+":"+map.get(msg.getKeys()));
return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
}
}
}
id为1和9同queue,所以1报错,id9一定不会消费,那么返回SUSPEND_CURRENT_QUEUE_A_MOMENT 表示等一会,再继续处理这批消息
根据试验,确实如此,重试默认时间间隔可以看出是1S。默认次数Integer.MAX
当然可以通过
consumer.setSuspendCurrentQueueTimeMillis(2000);
修改重试时间间隔
也可以通过
consumer.setMaxReconsumeTimes(10);
修改顺序消费重试次数
再试一次
可以看到这条有问题的消息直接抛掉,消费ID9的了
重试topic也是没有的
那么去哪了呢?我们在死信队列中找到
总结:
如果业务需求对于顺序消费 - 强要求,那么推荐在监听器中根据map中一定要根据重试次数采取一定措施,通知到人。不然会造成消息堆积
如果顺序要求不高,或者不想这么麻烦,可以设置重试次数,使队列可以正常下去,做好记录及抛掉此消息后对后续消息影响的判断。