基于RabbitMQ的TTL以及死信队列,使用SpringBoot实现延迟付款,手动补偿操作。
1、用户下单后展示等待付款页面
2、在页面上点击付款的按钮,如果不超时,则跳转到付款成功页面
3、如果超时,则跳转到用户历史账单中查看因付款超时而取消的订单。
生产者:
配置类
package com.lagou.config;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* @Date 2020/8/24 17:34
* @Created weijie
* @Description
*/
@Configuration
public class RabbitConfig {
@Bean
public Queue queue() {
Map<String, Object> props = new HashMap<>();
// 消息的生存时间 30s
props.put("x-message-ttl", 30000);
// 设置该队列所关联的死信交换器(当队列消息TTL到期后依然没有消费,则加入死信队列)
props.put("x-dead-letter-exchange", "ex.go.dlx");
// 设置该队列所关联的死信交换器的routingKey,如果没有特殊指定,使用原队列的routingKey
props.put("x-dead-letter-routing-key", "go.dlx");
Queue queue = new Queue("q.go", true, false, false, props);
return queue;
}
@Bean
public Queue queueDlx() {
Queue queue = new Queue("q.go.dlx", true, false, false);
return queue;
}
@Bean
public Exchange exchange() {
DirectExchange exchange = new DirectExchange("ex.go", true,
false, null);
return exchange;
}
/**
* 死信交换器
* @return
*/
@Bean
public Exchange exchangeDlx() {
DirectExchange exchange = new DirectExchange("ex.go.dlx", true,
false, null);
return exchange;
}
@Bean
public Binding binding() {
return
BindingBuilder.bind(queue()).to(exchange()).with("go").noargs();
}
/**
* 死信交换器绑定死信队列
* @return
*/
@Bean
public Binding bindingDlx() {
return
BindingBuilder.bind(queueDlx()).to(exchangeDlx()).with("go.dlx").noargs(
);
}
}
支付接口:
package com.lagou.controller;
import com.lagou.entity.Order;
import com.lagou.service.OrderService;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Date;
import java.util.List;
import java.util.UUID;
/**
* @Date 2020/8/23 22:27
* @Created by weijie
* @Description
*/
@Controller
public class OrderController {
@Autowired
AmqpTemplate amqpTemplate;
@Autowired
OrderService orderService;
int count = 0;
/**
* 下订单接口、支付接口、查看订单接口
*/
@RequestMapping("/order/create/orderId")
public void createOrder(@PathVariable String orderId){
Order order = new Order();
order.setOrderCode(UUID.randomUUID().toString());
order.setCreateTime(new Date());
order.setIsPay(0);
order.setOrderName("订单:" + count++);
amqpTemplate.convertAndSend("ex.go", "go", order);
orderService.update(order);
}
@RequestMapping("/pay/orderCode")
@ResponseBody
public String pay(@PathVariable String orderCode){
Order order = orderService.findByOrderCode(orderCode);
if (order == null){
return "订单不存在";
}
if (order.getIsPay() == 1){
return "订单已支付";
}
if (order.getStatus() == 1){
return "订单已超时";
}
//订单支付成功
order.setIsPay(1);
//未超时
order.setStatus(0);
orderService.update(order);
return "订单支付成功";
}
@RequestMapping("/order/list")
@ResponseBody
public List<Order> list(){
List<Order> all = orderService.findAll();
return all;
}
}
消费者
配置类:
package com.lagou.config;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Date 2020/8/24 15:19
* @Created weijie
* @Description
*/
@Configuration
public class RabbitConfig {
@Bean
public Queue queue(){
return QueueBuilder.nonDurable("q.go.dlx").build();
}
}
监听订单队列:
package com.lagou.consumer;
import com.lagou.entity.Order;
import com.lagou.service.OrderService;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* @Date 2020/8/24 15:35
* @Created weijie
* @Description
*/
@Component
public class Listener {
@Autowired
OrderService orderService;
private Integer index = 0;
/**
* 监听私信队里中的值
* @param msg
* @param channel
*/
@RabbitListener(queues = "q.go.dlx")
public void getMessage(Message msg, Channel channel) throws IOException {
String orderCode = new String(msg.getBody());
System.out.println("超时订单 : " + orderCode);
//需要确认收到的消息,否则死信队列消息会一直存在
//???
long deliveryTag = msg.getMessageProperties().getDeliveryTag();
if (index % 2 == 0){
//确认消息
channel.basicAck(deliveryTag, false);
}else{
//拒收消息
channel.basicAck(deliveryTag, false);
}
index ++;
Order order = orderService.findByOrderCode(orderCode);
if (order == null){
System.out.println("未查询到相应订单:" + orderCode);
return;
}
//订单已支付
if (order.getIsPay() > 0){
System.out.println("订单已支付:" + orderCode);
return;
}
//订单未支付
order.setIsPay(0);
//订单已超时
order.setIsTimeOut(1);
orderService.update(order);
}
}