demo地址:鏈接:https://pan.baidu.com/s/1kGaSCHlfhm6UvbPcRUYp4g
提取碼:11hs
訂單30分鐘未支付,系統自動超時關閉有哪些實現方案?
1.基於任務調度實現,效率是非常低,耗服務器性能
2.基於redis過期key實現.用戶下單的時候,生成一個令牌(有效期)30分鐘,存放到我們redis;
redis.set(orderToken ,orderID) 下單時候存放到redis,並存儲id入庫,30分鐘過期,
redis客戶端監聽,過期獲取到orderId,拿orderId去查訂單,沒有支付則,訂單關閉,庫存增加
缺點:非常冗餘 ,會在表中存放一個冗餘字段 https://blog.csdn.net/zhangshengqiang168/article/details/104925649
3.基於redis延遲隊列 https://blog.csdn.net/zhangshengqiang168/article/details/100130523
4.基於MQ的延遲隊列實現(最佳) 死信隊列(延遲隊列)
原理:當我們在下單的時候,往MQ投遞一個消息設置有效期爲30分鐘,但該消息失效的時候(沒有被消費的情況下),
執行我們客戶端一個方法告訴我們該消息已經失效,這時候查詢這筆訂單是否有支付.
實現原理(消息過期投遞到死信隊列)
原理:下單投放消息到A交換機(過期時間30分鐘),消息到aa隊列(綁定死信交換機),不設置aa隊列的消費者(故此消息一直未消費).
30分鐘後,過期消息投遞到死信交換機,死信隊列,由死信消費者消費,判斷訂單id是否支付,執行業務邏輯,支付->return
未支付->關閉訂單,返還庫存
demo演示:
1.聲明訂單和死信交換機、 訂單和死信隊列 、關鍵步驟訂單隊列綁定訂單交換機和死信交換機 、死信隊列綁定死信交換機
@Component
public class DeadLetterMQConfig {
/**
* 訂單交換機
*/
@Value("${mayikt.order.exchange}")
private String orderExchange;
/**
* 訂單隊列
*/
@Value("${mayikt.order.queue}")
private String orderQueue;
/**
* 訂單路由key
*/
@Value("${mayikt.order.routingKey}")
private String orderRoutingKey;
/**
* 死信交換機
*/
@Value("${mayikt.dlx.exchange}")
private String dlxExchange;
/**
* 死信隊列
*/
@Value("${mayikt.dlx.queue}")
private String dlxQueue;
/**
* 死信路由
*/
@Value("${mayikt.dlx.routingKey}")
private String dlxRoutingKey;
/**
* 聲明死信交換機
*/
@Bean
public DirectExchange dlxExchange() {
return new DirectExchange(dlxExchange);
}
/**
* 聲明死信隊列
*/
@Bean
public Queue dlxQueue() {
return new Queue(dlxQueue);
}
/**
* 聲明訂單業務交換機
*/
@Bean
public DirectExchange orderExchange() {
return new DirectExchange(orderExchange);
}
/**
* 聲明訂單隊列 核心操作一
*/
@Bean
public Queue orderQueue() {
Map<String, Object> arguments = new HashMap<>(2);
// 綁定我們的死信交換機
arguments.put("x-dead-letter-exchange", dlxExchange);
// 綁定我們的路由key
arguments.put("x-dead-letter-routing-key", dlxRoutingKey);
return new Queue(orderQueue, true, false, false, arguments);
}
/**
* 綁定訂單隊列到訂單交換機
*/
@Bean
public Binding orderBinding() {
return BindingBuilder.bind(orderQueue()).to(orderExchange()).with(orderRoutingKey);
}
/**
* 綁定死信隊列到死信交換機
*/
@Bean
public Binding binding() {
return BindingBuilder.bind(dlxQueue()).to(dlxExchange()).with(dlxRoutingKey);
}
}
2.application.properties文件配置 交換機隊列相關名稱和其他配置
server.port=8082
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#開啓駝峯命名 譬如數據庫create_time 自動映射pojo屬性createTime
mybatis.configuration.map-underscore-to-camel-case=true
#配置virtual-host虛擬主機
spring.rabbitmq.virtual-host=/zhang_rabbit
#ip地址
spring.rabbitmq.host=127.0.0.1
#用戶名 密碼
spring.rabbitmq.username=zhang
spring.rabbitmq.password=zhang
#連接端口號
spring.rabbitmq.port=5672
#spring.rabbitmq.publisher-confirm-type=
#模擬演示死信隊列
mayikt.dlx.exchange=mayikt_order_dlx_exchange
mayikt.dlx.queue=mayikt_order_dlx_queue
mayikt.dlx.routingKey=dlx
##備胎交換機
mayikt.order.exchange=mayikt_order_exchange
mayikt.order.queue=mayikt_order_queue
mayikt.order.routingKey=mayikt.order
3.創建producer生產者.暫時設置消息10秒過期,驗證消息是否加入死信隊列
@RestController
public class OrderController {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RabbitTemplate rabbitTemplate;
@Value("${mayikt.order.exchange}")
private String orderExchange; //訂單交換機
@Value("${mayikt.order.routingKey}")
private String orderRoutingKey; //訂單路由key
@GetMapping("/addOrder")
public String addOrder(){
String orderId=System.currentTimeMillis()+"";
OrderEntity orderEntity=new OrderEntity("訂單30分鐘過期",orderId,0);
//訂單入庫
int result= orderMapper.addOrder(orderEntity);
if(result<=0){
return "fail";
}
//rabbit投遞消息
rabbitTemplate.convertAndSend(orderExchange,orderRoutingKey,orderId,messagePostProcessor());
return "success";
}
//處理待發送消息
private MessagePostProcessor messagePostProcessor(){
return new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
//設置有效期30分鐘
//message.getMessageProperties().setExpiration("1800000");
message.getMessageProperties().setExpiration("10000");
return message;
}
};
}
}
4.死信消費者消費過期消息
@Component //死信隊列
public class OrderDlxConsumer {
@Autowired
private OrderMapper orderMapper;
/**
* 監聽我們的死信隊列
*/
@RabbitListener(queues = "mayikt_order_dlx_queue")
public void orderConsumer(String orderId) {
System.out.println("死信隊列獲取消息:" + orderId);
if (StringUtils.isEmpty(orderId)) {
return;
}
//根據id查詢
OrderEntity orderEntity = orderMapper.getOrder(orderId);
if (null == orderEntity) {
return;
}
//獲取狀態
Integer orderStatus=orderEntity.getOrderStatus();
//判斷未支付 , 關閉訂單
if(0==orderStatus){
orderMapper.updateStatus(orderId,2);
//庫存返還
}
}
}
5.下單測試 http://localhost:8082/addOrder.訂單入庫成功.狀態0未支付
10秒後,死信消費者處理 狀態0未支付 變爲2 已關閉