RocketMq消息Tag
业务相近但有区别,主题一致,可用tag,用tag区分同一主题的不同消息

发送消息:创建消息对象时再写一个tag就行了
@Test
void tagProducer() throws Exception{
DefaultMQProducer orderProducer = new DefaultMQProducer("tag-order-Producer");
orderProducer.setNamesrvAddr(mqConstant.Name_Serve_Addr);
orderProducer.start();
//创建消息
Message vip1Message = new Message("tag-order-topic", "vip1", "我的vip1的文章".getBytes());
Message vip2Message = new Message("tag-order-topic", "vip2", "我的vip2的文章".getBytes());
SendResult send = orderProducer.send(vip1Message);
SendResult send1 = orderProducer.send(vip2Message);
log.info(send.getSendStatus().toString());
log.info(send1.getSendStatus().toString());
orderProducer.shutdown();
}
消费者代码如下vip1Consumer.subscribe("tag-order-topic","vip1");在订阅消息时换掉*就行了
/**
* 订阅vip1
* @throws Exception
*/
@Test
void consumeVip1() throws Exception{
DefaultMQPushConsumer vip1Consumer = new DefaultMQPushConsumer("tag-order-consumer-A");
vip1Consumer.setNamesrvAddr(mqConstant.Name_Serve_Addr);
vip1Consumer.subscribe("tag-order-topic","vip1");
vip1Consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
log.info("我在消费vip1");
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
}
/**
* 订阅vip2 vip1
* @throws Exception
*/
@Test
void consumeVip2_Vip2() throws Exception{
DefaultMQPushConsumer vip1_2Consumer = new DefaultMQPushConsumer("tag-order-consumer-B");
vip1_2Consumer.setNamesrvAddr(mqConstant.Name_Serve_Addr);
vip1_2Consumer.subscribe("tag-order-topic","vip1||vip2");
vip1_2Consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
log.info("我在消费vip1和vip2");
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
}
RocketMq什么时候用Topic 什么时候用Tag
1.不同的消息类型:如普通消息,延迟消息,顺序消息等不同的消息类型使用不同的topic,无法通过tag进行区分
2.业务不关联用topic 如果都是订单,但是是不同项目的订单,就用tag
3.消息的优先级是否不同,不同的优先级用不同的topic
4.消息的量级是否相当,有的业务量小时效性高,有的量特别大,放一个topic可能会因为过长等待而饿死
RocketMq 消息Key
在RocketMq中的消息,默认会有一个MessageId当做消息的唯一标识,我们也可以给消息携带一个key,用作唯一标识或者业务标识,包括在控制面板查询的时候也可以使用Messageid和key来查询

创建消息的时候指定一下key就行了,业务参数我们自己程序要保证唯一,为了查阅以及后期的去重
String uuid = UUID.randomUUID().toString();
//创建消息
Message vip1Message = new Message("tag-order-topic", "vip1",uuid ,"我的vip1的文章".getBytes());
消费者,key是在Message里面
vip1_2Consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
log.info("我在消费vip1和vip2");
MessageExt messageExt = list.get(0);
//获取key
String keys = messageExt.getKeys();
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
RocketMq重复消费问题

1、生产者多次投递:mq返回ack时消息丢失,生产者以为投递失败就再投一次
2、消费者扩容重试,在负载均衡情况下,原来只有C1,C1拿到A信息时,还没有消费完,位点还在A,突然C2来了,会和C1负载均衡,C2分到01队列,C2会重新消费一遍A

如何判断这个消息的key是否存在
在消费的时候怎么判断目前这个key之前有没有消费过呢?
就需要一个容器来存放key 一般使用redis 或者 MySQL
插述:幂等性:多次操作产生的影响和第一次操作产生的影响相同
下图是使用数据库记录,这个数据库表叫去重表,但是并发时就会出问题,先执行业务逻辑再插入key,会重复只想业务逻辑的,要如何改正呢

利用唯一索引,将key的字段在数据库设置为唯一索引,取消查询直接插入key即可key插入成功就说明没有消费,插入失败就是已经有了,抛出异常即可,就不会再执行业务逻辑了
下面是特别关键一点,如果发生主键冲突,只是抛出异常的话,重复消息又会回到消息队列里面,重复投递就会发生问题,所以要捕获唯一索引异常SQLIntegrityConstraintViolationException然后返回消息成功就可以了

消息重试和死信问题
生产者重试,可以设置发送失败后,重试的次数

消费者重试,业务抛异常,return null return RECONSUME_LATER会重试,但是重试不是发生问题就立刻重试,会有一定时间差,因为要先回队里再投递,重试的时间间隔和延迟消息的时间间隔是一致的,默认重试16次,从10s这个等级开始加

一个MessageExt类里面有什么信息呢?即一个消息对象里面有什么信息

1、如果最大重试次数都是失败的该怎么做?(死信处理)
并发模式下是16次 顺序模式是int的最大值,或者达到自定义次数都是失败的,那么这个消息就是死信
这个死信消息就会被放在另外一个队列叫做死信队列 Topic为%DLQ%消费者组名,这个组里面只有一个队列,然后再启动一个死信消费者处理死信信息即可

//那如果死信的topic太多要启动很多消费者嘛?
第二种方案,在正常消费者里面处理死信,获取Message的次数,判断一下即可

2、重试16次太久了,我想自定义重试次数怎么做?
在注册消息监听前可以设置最大消费次数

RocketMQ核心概念与实战
1033

被折叠的 条评论
为什么被折叠?



