java 30分钟后删除订单_超30min订单自动取消用rabbitmq怎么做

超30min订单自动取消,RabbitMQ做延迟队列,下单成功把订单号推入RabbitMQ,超30min订单自动进入DLX死信队列,消费端监听死信队列得到超时订单,订单状态置为超时,这个已完成,目前卡在30min内支付(不超时)时如何取消相应的订单?即如何在RabbitMQ中删除指定的消息?消息确认后会清除,怎样确认指定的消息?不知道我的思路有问题还是怎样,目前卡在这了,请各位大神指点一下

相关代码

下单

@Override

@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)

public Result order(Long productId, Long userId) {

// redis中商品名

String productNameKey = "PRODUCT_NAME_" + productId;

// redis中商品库存

String productNumberKey = "PRODUCT_" + productId;

// redis中已抢购成功的用户ID

String userIdKey = "USER_" + productId;

String productName = redisUtil.get(productNameKey, 0);

// 缓存中有商品

if (NumberUtils.toInt(redisUtil.get(productNumberKey, 0)) > 0) {

// 当前用户是否已抢购成功

if (!redisUtil.sismember(userIdKey, userId + "")) {

// 减缓存

redisUtil.decr(productNumberKey);

// 减库存

boolean flag = productMapper.reduceRepertory(productId) == 1;

if (flag) {

// 生成订单

Order order = new Order();

// 订单号

String orderNo = "ORDER_" + DATE_TIME_FORMATTER.format(LocalDateTime.now()) + "_" + productId + "_" + userId;

order.setOrderNo(orderNo);

order.setOrderPrice(new BigDecimal(100));

order.setOrderStatus(1);

order.setUserId(userId);

save(order);

// 建立订单商品关系

ProductOrder productOrder = new ProductOrder();

productOrder.setOrderId(order.getId());

productOrder.setProductId(productId);

productOrderMapper.insert(productOrder);

// 缓存userId

redisUtil.sadd(userIdKey, userId + "");

// 订单号orderNo 加入MQ, 30分钟未支付加入死信队列

messageProducer.sendMessage(orderNo, 30 * 60 * 1000);

return Result.success("下单成功, 订单号" + order.getId());

} else {

Product product = productMapper.selectOne(new QueryWrapper().eq("id", productId));

// 减库存失败,重置缓存

redisUtil.set(productNumberKey, JacksonUtil.toJson(product.getProductNumber()), 0);

log.error("用户{}下单失败, 商品名: {}", userId, productName);

return Result.error("202", "用户" + userId + "下单失败, 商品名" + productName);

}

} else {

// 该用户已抢到商品

log.error("您已抢到商品{},订单已生成,请去收银台支付", productName);

return Result.error("203", "您已抢到商品" + productName + ",订单已生成,请去收银台支付");

}

} else {

// 商品售罄

log.error("商品{}已被抢光", productName);

return Result.error("201", "商品" + productName + "已被抢光");

}

}

支付,这里如何删除RabbitMQ中指定的消息(下单时把订单都投入RabbitMQ里了)

@Override

public Result pay(String orderNo, Long userId) {

Order order = orderMapper.selectOne(new QueryWrapper().eq("order_no", orderNo).eq("user_id", userId));

order.setOrderStatus(2); // 已支付

orderMapper.updateById(order);

// MQ中删除支付成功的订单 TODO

return Result.success("订单" + orderNo + "支付成功");

}

发消息

@Component

@Slf4j

public class MessageProducer {

@Resource

private RabbitTemplate rabbitTemplate;

/**

* 发送消息

*

* @param message 消息

* @param ttl 有效时间

*/

public void sendMessage(String message, int ttl) {

CorrelationData correlationId = new CorrelationData(UUID.randomUUID().toString());

rabbitTemplate.convertAndSend(RabbitConfig.EXCHANGE_TTL, RabbitConfig.ROUTINGKEY_TTL, message, message1 -> {

MessageProperties messageProperties = message1.getMessageProperties();

messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT); // 消息持久化

messageProperties.setExpiration(ttl + ""); // 消息TTL,单位毫秒

return message1;

}, correlationId);

log.info("发送消息: {}, 有效时间: {}秒, 回调ID: {}, 当前时间: {}", message, ttl / 1000, correlationId, LocalDateTime.now().toString());

}

}

rabbit配置,声明TTL交换器及对应队列,声明AE备份交换器及对应队列绑定到TTL交换器上,保证消息路由失败不丢失,声明DLX死信交换器及队列绑定到TTL交换器上,实现延迟效果

@Configuration

@Slf4j

public class RabbitConfig {

@Value("${spring.rabbitmq.addresses}")

private String addresses;

@Value("${spring.rabbitmq.username}")

private String username;

@Value("${spring.rabbitmq.password}")

private String password;

@Value("${spring.rabbitmq.virtual-host}")

private String virtualHost;

@Value("${spring.rabbitmq.publisher-confirms}")

private Boolean publisherConfirms;

@Value("${spring.rabbitmq.publisher-returns}")

private Boolean publisherReturns;

/**

* 备份交换器

*/

public static final String EXCHANGE_AE = "EXCHANGE_AE";

/**

* TTL交换器

*/

public static final String EXCHANGE_TTL = "EXCHANGE_TTL";

/**

* 死信交换器

*/

public static final String EXCHANGE_DLX = "EXCHANGE_DLX";

/**

* 备份队列

*/

public static final String QUEUE_AE = "QUEUE_AE";

/**

* TTL队列

*/

public static final String QUEUE_TTL = "QUEUE_TTL";

/**

* 死信队列

*/

public static final String QUEUE_DLX = "QUEUE_DLX";

/**

* TTL Routing_Key

* 特殊字符“*”与“#”,用于做模糊匹配,其中“*”用于匹配一个单词,“#”用于匹配多个单词(可以是零个)

*/

public static final String ROUTINGKEY_TTL = "routing.ttl.#";

/**

* 死信Routing_Key

*/

public static final String ROUTINGKEY_DLX = "routing.dlx";

@Resource

private PublishConfirm publishConfirm;

@Resource

private PublishReturnCallBack publishReturnCallBack;

@Bean

public ConnectionFactory connectionFactory() {

CachingConnectionFactory connectionFactory = new CachingConnectionFactory();

connectionFactory.setAddresses(addresses);

connectionFactory.setVirtualHost(virtualHost);

connectionFactory.setUsername(username);

connectionFactory.setPassword(password);

// 消息发送到RabbitMQ交换器后接收ack回调

connectionFactory.setPublisherConfirms(publisherConfirms);

// 消息发送到RabbitMQ交换器,但无相应队列与交换器绑定时的回调

connectionFactory.setPublisherReturns(publisherReturns);

return connectionFactory;

}

@Bean

// @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) //必须是prototype类型

public RabbitTemplate rabbitTemplate() {

RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory());

rabbitTemplate.setConfirmCallback(publishConfirm);

rabbitTemplate.setReturnCallback(publishReturnCallBack);

return rabbitTemplate;

}

/**

* 备份队列常用FanoutExchange

*/

@Bean

public FanoutExchange alternateExchange() {

return new FanoutExchange(EXCHANGE_AE);

}

/**

* TTL队列, 指定AE

*/

@Bean

public TopicExchange ttlExchange() {

Map args = new HashMap<>();

args.put("alternate-exchange", EXCHANGE_AE);

return new TopicExchange(EXCHANGE_TTL, true, false, args);

}

/**

* 死信交换器

*/

@Bean

public DirectExchange deathLetterExchange() {

return new DirectExchange(EXCHANGE_DLX);

}

/**

* 备份队列

*/

@Bean

public Queue queueAE() {

// return new Queue(QUEUE_AE, true); //队列持久

return QueueBuilder.durable(QUEUE_AE).build();

}

/**

* TTL队列,指定DLX

*/

@Bean

public Queue queueTTL() {

Map args = new HashMap<>();

// x-dead-letter-exchange 声明了队列里的死信转发到的DLX名称,

args.put("x-dead-letter-exchange", EXCHANGE_DLX);

// x-dead-letter-routing-key 声明了这些死信在转发时携带的 routing-key 名称。

args.put("x-dead-letter-routing-key", ROUTINGKEY_DLX);

// return new Queue(QUEUE_TTL, true, false, false, args);

return QueueBuilder.durable(QUEUE_TTL).withArguments(args).build();

}

/**

* 死信队列

*/

@Bean

public Queue queueDLX() {

// return new Queue(QUEUE_DLX, true); //队列持久

return QueueBuilder.durable(QUEUE_DLX).build();

}

/**

* AE绑定队列

*/

@Bean

public Binding bindingAE() {

return BindingBuilder.bind(queueAE()).to(alternateExchange());

}

/**

* TTL绑定队列

*/

@Bean

public Binding bindingTTL() {

return BindingBuilder.bind(queueTTL()).to(ttlExchange()).with(ROUTINGKEY_TTL);

}

/**

* DLX绑定队列

*/

@Bean

public Binding bindingDLX() {

return BindingBuilder.bind(queueDLX()).to(deathLetterExchange()).with(ROUTINGKEY_DLX);

}

监听死信队列,获取超时订单(未实现,目前只是demo)

@RabbitHandler // 声明接收方法

@RabbitListener(queues = {RabbitConfig.QUEUE_DLX})

public void processDLX(Message message, Channel channel) {

String payload = new String(message.getBody());

log.info("接收处理DLX队列当中的消息: {}, 当前时间: {}", payload, LocalDateTime.now().toString());

try {

// TODO 通知 MQ 消息已被成功消费,可以ACK了

channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);

} catch (IOException e) {

// TODO 如果报错了,那么我们可以进行容错处理,比如转移当前消息进入其它队列

}

}

另外,百度上找到的一个项目RabbitTemplate有这样的配置(RabbitTemplate必须是prototype类型) ,其他人项目并没有这个配置,想知道到底怎样才算对,小白一个,望大神多多指点

@Bean

// @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) //必须是prototype类型

public RabbitTemplate rabbitTemplate() {

RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory());

rabbitTemplate.setConfirmCallback(publishConfirm);

rabbitTemplate.setReturnCallback(publishReturnCallBack);

return rabbitTemplate;

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值