一、业务场景
我们在做关于商城系统可能就会出现一个场景:用户下单后,生成了一个订单,但没有对该订单付款,就会出现订单状态一直卡在待付款状态,相对应的需求就是我们在创建订单的一段时间后,我们需要把订单状态改成未取消,所以引入了RabbitMQ来实现这个功能。
二、实现
我们系统是基于SpringBoot来做的开发,以下代码仅供参考,后续健全需要根据自己的业务需求进行完善。
1、引入依赖
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-amqp</artifactId>
<version>2.4.12</version>
</dependency>
2、配置YML文件
spring:
# mq的配置
rabbitmq:
host: 192.168.200.130 // 地址
port: 5672 // 端口号
username: admin // RabbitMQ账号
password: 123456 // RabbitMQ密码
3、MQ的配置类
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class OrderMqConfig {
//普通交换机名称
public static final String ORDER_CREATE_EXCHANGE="order_create_exchange";
//死信交换机的名称
public static final String ORDER_DEAD_LETTER_EXCHANGE="order_dead_exchange";
//普通队列的名称
public static final String QUEUE_ORDER="order_queue";
//死信队列名称
public static final String DEAD_ORDER_QUEUE="order_dead_queue";
@Bean("orderCreateExchange")
public DirectExchange orderCreateExchange(){
return new DirectExchange(ORDER_CREATE_EXCHANGE);
}
@Bean("orderDeadExchange")
public DirectExchange orderDeadExchange(){
return new DirectExchange(ORDER_DEAD_LETTER_EXCHANGE);
}
//声明队列
@Bean("orderQueue")
public Queue orderQueue(){
Map<String,Object> arguments=new HashMap<>(3);
//设置死信交换机
arguments.put("x-dead-letter-exchange",ORDER_DEAD_LETTER_EXCHANGE);
//设置死信RoutingKey
arguments.put("x-dead-letter-routing-key","YD");
//设置过期时间 单位是ms,这里可以根据自己业务时间设置为超时的时间,这里是10s
arguments.put("x-message-ttl",10000);
return QueueBuilder.durable(QUEUE_ORDER)
.withArguments(arguments).build();
}
//死信队列
@Bean("orderDeadQueue")
public Queue orderDeadQueue(){
return QueueBuilder.durable(DEAD_ORDER_QUEUE).build();
}
//绑定
// @Qualifier是对应上面bean的别名
@Bean
public Binding orderQueueBingOrderCreateExchange(@Qualifier("orderQueue")Queue queueA,
@Qualifier("orderCreateExchange")DirectExchange xExchange){
return BindingBuilder.bind(queueA).to(xExchange).with("XA");
}
@Bean
public Binding orderDeadQueueBingOrderDeadExchange(@Qualifier("orderDeadQueue")Queue queueD,
@Qualifier("orderDeadExchange")DirectExchange xExchange){
return BindingBuilder.bind(queueD).to(xExchange).with("YD");
}
}
代码架构图
实现方式:
1、创建两个交换机和两个队列(一个普通的交换机和一个普通队列,一个死信交换机和一个死信队列),死信顾名思义就是普通队列未消费的东西就到了死信队列进行消费。
2、配置的时候用到了延时消息,这里需要安装一下RabbitMQ延迟队列插件。
RabbitMQ控制台的变化
4、发送消息
@Resource
private RabbitTemplate rabbitTemplate;
/**
* 创建订单
* @param order
* @return
*/
@Override
@Transactional
public AjaxResult createOrder(Order order) {
// 根据业务需求写创建订单的逻辑
// 新增订单
orderService.insertOrder(order);
// 发送消息
rabbitTemplate.convertAndSend("order_create_exchange","XA",order);
}
5、消费消息
import com.ruoyi.second.domain.Order;
import com.ruoyi.second.service.IOrderService;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class DeadLetterQueueConsumer {
@Autowired
private IOrderService orderService;
//接收消息 监听的队列为死信队列消费
@RabbitListener(queues = "order_dead_queue")
public void receiveD(Order order) throws Exception{
// 说明消息未消费,进入了死信队列
System.out.println("order = " + order);
Long orderId = order.getOrderId();
Order orderOne = orderService.selectOrderByOrderId(orderId);
// 说明还没消费
if (orderOne.getOrderPayState().equals("0")){
// 状态改成订单已取消
orderOne.setOrderState("4");
orderService.updateOrder(orderOne);
}
}
}
三、总结
1、实现方式是基于RabbitMQ的延迟队列(属于死信队列的一种),主要是创建一个普通队列和普通交换机,如果普通队列超过了我们设置的时间后没有消费这条消息,那么这条消息就会进入到死信交换机由死信队列进行消费。
2、以上代码是比较简单的业务逻辑实现,具体实现应该根据需求而改变,例外应该对RabbitMQ做一些可靠性的改变,例如考虑到发送消息的时候突然因为某种原因机器挂掉导致消息丢失应该怎么处理;消息从交换机到队列消费的过程中丢失应该怎么处理等等....