这是我参与8月更文挑战的第21天,活动详情查看:8月更文挑战
关于RabbitMQ可靠性投递现在主要有两种方案,对于下面介绍的
“消息的延迟投递,做二次确认,回调检查”
这种方案在网上的实际例子比较少,只是简单描述一下过程,给的流程图也大都一样,比较难以理解。下面是我结合网上资料给出自己的理解,如果有错误的地方,还请留言指出👼
在上一篇文章中RabbitMQ生产端可靠性投递(一),我们采用定时任务+消息落库、打标的方式保证生产端可靠性投递,其中需要执行两次数据入库,在高并发场景下会存在性能瓶颈。因此,在这篇文章中将介绍另外一种可靠性投递方案:消息的延迟投递,做二次确认,回调检查,实现过程如下图:
- 过程描述:
-
首先生产者先完成业务数据的入库,然后进行第一次消息的发送。🚨一定要等业务数据入库成功后再发送消息。
-
完成第一次消息发送后,生产者再一次发送一条消息到指定过期时间的队列中,比如设置5分钟后消息过期,过期后消息会被重新路由到死信队列,监听者2会监听该死信队列,来获取第二次投递的消息。
-
消费者会监听指定的队列即队列1,消费者接收第一次发送的消息
-
如果消费者接收到消息并完成消费后,回送响应,这里的回送响应不是一般的返回确认ack,而是消费者会发送一条Comfirm消息到指定的队列,即队列2。
-
监听者1会监听队列2,获取消费者返回的Confirm。
-
监听者1接收到Comfrim后,会将消息保存到MsgDB消息数据库中,表示该消息已经被正常投递且被消费者消费了。
-
第一条消息发送5分钟后,第二次发送的消息会被重新路由到死信队列,即队列3中,监听者2监听队列3,并获取第二次发送的消息,然后根据该消息从消息数据库MsgDB中判断该消息是否在数据库中已经存在。不存在,则说明之前的消息没有被消费成功,则需要通知生产者重新发送消息。如果数据库中存在该消息,则说明第一次发送的消息已经被消费者成功消费,则无需做任何操作,消息投递成功。
Demo
方案中的延迟投递我们以TTL+DXL来实现。
- 声明队列、交换机、死信队列,死信交换机,并将他们按上图的路由键进行绑定。
@Bean
public DirectExchange directExchange(){
return new DirectExchange("direct-Ex",true,false);
}
@Bean
public Queue queue1(){
return new Queue("queue1",true,false,false);
}
@Bean
public Queue queue2(){
return new Queue("queue2",true,false,false);
}
@Bean
public Binding bindQueue1(){
return BindingBuilder.bind(queue1()).to(directExchange()).with("m1");
}
@Bean
public Binding bindQueue2(){
return BindingBuilder.bind(queue2()).to(directExchange()).with("Lis2");
}
//定义一个死信交换机
@Bean
public DirectExchange deadLetterExchange(){
return new DirectExchange("deadLetterExchange",true,false);
}
//定义指定消息过期时间的ttl队列
@Bean
public Queue ttlQueue(){
HashMap<String, Object> args = new HashMap<>();
args.put("x-message-ttl",60000);
args.put("x-dead-letter-exchange","deadLetterExchange");
args.put("x-dead-letter-routing-key","dead");
return new Queue("ttlQueue",true,false,false,args);
}
//将ttl队列绑定到交换机direct-Ex上面,binding key 为ttl
@Bean
public Binding bindTtlQueue(){
return BindingBuilder.bind(ttlQueue()).to(directExchange()).with("ttl");
}
//定义队列3,即死信队列
@Bean
public Queue queue3(){
return new Queue("queue3",true,false,false);
}
//将队列3绑定到死信交换机上,binding key为dead
@Bean
public Binding bindDeadLetterQueue(){
return BindingBuilder.bind(queue3()).to(deadLetterExchange()).with("dead");
}
- 定义监听者1和监听者2,分别监听队列queue2、queue3
- 定义消费者,监听队列1,如果成功接收到消息并消费,则发送一条Comfirm消息到MQ中。
4.Controller接收请求,进行业务处理和消息发送
- Postman测试
- 运行结果
🏁以上就是对生产端可靠性投递另一种方案的简单介绍,如果有错误的地方,还请留言指正,如果觉得本文对你有帮助那就点个赞👍吧😋😻😍