springboot整合rabbitmq實(shí)現(xiàn)訂單超時(shí)取消案例分析
訂單超時(shí)取消案例,詳細(xì)請(qǐng)往下看~~~
1. RabbitMQ 配置類
RabbitMQConfig.java
這個(gè)類負(fù)責(zé)定義RabbitMQ的交換機(jī)、隊(duì)列和綁定配置。
import org.springframework.amqp.core.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitMQConfig { public static final String ORDER_EXCHANGE = "order-exchange"; public static final String ORDER_QUEUE = "order-queue"; public static final String ORDER_ROUTING_KEY = "order-routing-key"; public static final String TTL_ORDER_QUEUE = "ttl-order-queue"; public static final String TTL_ORDER_ROUTING_KEY = "ttl-order-routing-key"; // 定義一個(gè)Direct類型的交換機(jī) @Bean public DirectExchange orderExchange() { return new DirectExchange(ORDER_EXCHANGE); } // 定義一個(gè)普通的隊(duì)列,用于接收實(shí)際訂單處理的消息 @Bean public Queue orderQueue() { return QueueBuilder.durable(ORDER_QUEUE).build(); } // 定義一個(gè)TTL(時(shí)間到期)隊(duì)列,消息會(huì)在這個(gè)隊(duì)列中等待TTL后轉(zhuǎn)發(fā)到實(shí)際處理隊(duì)列 @Bean public Queue ttlOrderQueue() { return QueueBuilder.durable(TTL_ORDER_QUEUE) .withArgument("x-dead-letter-exchange", ORDER_EXCHANGE) // 設(shè)置死信交換機(jī) .withArgument("x-dead-letter-routing-key", ORDER_ROUTING_KEY) // 設(shè)置死信路由鍵 .withArgument("x-message-ttl", 60000) // 設(shè)置TTL為60秒 .build(); } // 將實(shí)際處理隊(duì)列綁定到交換機(jī) @Bean public Binding orderBinding() { return BindingBuilder.bind(orderQueue()).to(orderExchange()).with(ORDER_ROUTING_KEY); } // 將TTL隊(duì)列綁定到交換機(jī) @Bean public Binding ttlOrderBinding() { return BindingBuilder.bind(ttlOrderQueue()).to(orderExchange()).with(TTL_ORDER_ROUTING_KEY); } }
詳細(xì)解釋:
交換機(jī)(Exchange)
orderExchange
:定義了一個(gè)DirectExchange
類型的交換機(jī)order-exchange
。- Direct類型的交換機(jī)會(huì)根據(jù)路由鍵(routing key)精確匹配消息隊(duì)列。
隊(duì)列(Queue)
orderQueue
:定義了一個(gè)普通的隊(duì)列order-queue
,這個(gè)隊(duì)列用于接收和處理訂單消息。ttlOrderQueue
:定義了一個(gè)TTL隊(duì)列ttl-order-queue
,這個(gè)隊(duì)列設(shè)置了TTL(x-message-ttl)為60秒。當(dāng)消息在這個(gè)隊(duì)列中超過(guò)60秒未被消費(fèi),它會(huì)變成死信消息(Dead Letter),然后根據(jù)配置的死信交換機(jī)(x-dead-letter-exchange)和死信路由鍵(x-dead-letter-routing-key)轉(zhuǎn)發(fā)到指定的隊(duì)列。
綁定(Binding)
orderBinding
:將order-queue
隊(duì)列綁定到order-exchange
交換機(jī),使用路由鍵order-routing-key
。ttlOrderBinding
:將ttl-order-queue
隊(duì)列綁定到order-exchange
交換機(jī),使用路由鍵ttl-order-routing-key
。這意味著發(fā)送到這個(gè)路由鍵的消息會(huì)首先進(jìn)入TTL隊(duì)列。
2. 訂單服務(wù)
OrderService.java
這個(gè)類負(fù)責(zé)訂單的創(chuàng)建和付款邏輯。
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.core.AmqpTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.UUID; @Service public class OrderService { private static final Logger logger = LoggerFactory.getLogger(OrderService.class); @Autowired private AmqpTemplate amqpTemplate; // 創(chuàng)建訂單并發(fā)送消息到TTL隊(duì)列 public void createOrder(String orderId) { logger.info("創(chuàng)建訂單: {}", orderId); amqpTemplate.convertAndSend(RabbitMQConfig.ORDER_EXCHANGE, RabbitMQConfig.TTL_ORDER_ROUTING_KEY, orderId); } // 支付訂單 public void payOrder(String orderId) { logger.info("支付訂單: {}", orderId); // 訂單支付邏輯 // 支付成功后,需要取消TTL隊(duì)列中的消息,防止訂單被取消 // 可以通過(guò)業(yè)務(wù)邏輯來(lái)實(shí)現(xiàn),比如數(shù)據(jù)庫(kù)狀態(tài)變化 } }
詳細(xì)解釋:
createOrder
方法:當(dāng)創(chuàng)建一個(gè)訂單時(shí),會(huì)生成一個(gè)唯一的訂單ID,并將其發(fā)送到order-exchange
交換機(jī),使用ttl-order-routing-key
路由鍵。這會(huì)將消息放入TTL隊(duì)列ttl-order-queue
。payOrder
方法:模擬支付訂單的過(guò)程。支付成功后,需要在業(yè)務(wù)邏輯中處理,確保訂單不會(huì)被超時(shí)取消。這個(gè)例子沒(méi)有實(shí)現(xiàn)具體的取消邏輯,但在實(shí)際應(yīng)用中,可以通過(guò)數(shù)據(jù)庫(kù)或其他機(jī)制來(lái)實(shí)現(xiàn)。
3. 超時(shí)監(jiān)聽(tīng)器
OrderTimeoutListener.java
這個(gè)類負(fù)責(zé)監(jiān)聽(tīng)超時(shí)消息并取消訂單。
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component public class OrderTimeoutListener { private static final Logger logger = LoggerFactory.getLogger(OrderTimeoutListener.class); // 監(jiān)聽(tīng)來(lái)自order-queue隊(duì)列的消息 @RabbitListener(queues = RabbitMQConfig.ORDER_QUEUE) public void handleOrderTimeout(String orderId) { logger.info("訂單超時(shí)未支付,取消訂單: {}", orderId); // 取消訂單的業(yè)務(wù)邏輯 } }
詳細(xì)解釋:
handleOrderTimeout
方法:監(jiān)聽(tīng)order-queue
隊(duì)列中的消息。- 當(dāng)TTL時(shí)間到期后,消息會(huì)被轉(zhuǎn)發(fā)到這個(gè)隊(duì)列,然后這個(gè)方法會(huì)被觸發(fā),處理訂單超時(shí)取消的業(yè)務(wù)邏輯。
4. 主應(yīng)用程序
RabbitMqOrderApplication.java
這個(gè)類是Spring Boot的主應(yīng)用程序類,包含了啟動(dòng)邏輯和示例訂單處理流程。
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class RabbitMqOrderApplication implements CommandLineRunner { @Autowired private OrderService orderService; public static void main(String[] args) { SpringApplication.run(RabbitMqOrderApplication.class, args); } @Override public void run(String... args) throws Exception { String orderId = UUID.randomUUID().toString(); orderService.createOrder(orderId); // 模擬延遲支付 Thread.sleep(30000); // 30秒后支付 orderService.payOrder(orderId); } }
詳細(xì)解釋:
CommandLineRunner
接口:實(shí)現(xiàn)了這個(gè)接口的run
方法會(huì)在Spring Boot應(yīng)用啟動(dòng)后立即執(zhí)行。run
方法:生成一個(gè)唯一的訂單ID,調(diào)用orderService.createOrder
方法創(chuàng)建訂單,并將訂單消息發(fā)送到TTL隊(duì)列。然后,模擬延遲30秒后調(diào)用orderService.payOrder
方法支付訂單。
總結(jié)
在這個(gè)示例中,我們展示了如何使用Spring Boot和RabbitMQ實(shí)現(xiàn)一個(gè)簡(jiǎn)單的訂單超時(shí)取消功能。通過(guò)配置TTL隊(duì)列和死信交換機(jī),可以有效地管理訂單的超時(shí)邏輯。
實(shí)際應(yīng)用中,可以根據(jù)具體需求調(diào)整TTL時(shí)間和業(yè)務(wù)邏輯處理訂單狀態(tài)。
在支付成功后需要取消TTL隊(duì)列中的消息,防止訂單被取消,可以通過(guò)以下幾種方法來(lái)實(shí)現(xiàn):
方法一:使用數(shù)據(jù)庫(kù)標(biāo)記和業(yè)務(wù)邏輯過(guò)濾
1.數(shù)據(jù)庫(kù)標(biāo)記訂單狀態(tài):
- 在訂單數(shù)據(jù)庫(kù)中添加一個(gè)字段來(lái)標(biāo)記訂單狀態(tài),例如
status
字段,狀態(tài)值可以是NEW
、PAID
、CANCELLED
等。 - 當(dāng)訂單支付成功后,將訂單狀態(tài)更新為
PAID
。
2.業(yè)務(wù)邏輯過(guò)濾:
- 在處理超時(shí)消息時(shí),首先檢查訂單的狀態(tài),如果訂單已經(jīng)支付(狀態(tài)為
PAID
),則忽略取消操作。
方法二:使用消息確認(rèn)機(jī)制(ACK/NACK)
手動(dòng)ACK消息:
- 配置RabbitMQ的消息監(jiān)聽(tīng)器,使其使用手動(dòng)確認(rèn)(ACK)模式。
- 當(dāng)訂單支付成功時(shí),通過(guò)業(yè)務(wù)邏輯顯式地確認(rèn)消息,這樣RabbitMQ就不會(huì)將消息重新發(fā)送。
具體實(shí)現(xiàn)示例
下面我們?cè)敿?xì)介紹如何實(shí)現(xiàn)這兩種方法。
方法一:使用數(shù)據(jù)庫(kù)標(biāo)記和業(yè)務(wù)邏輯過(guò)濾
1. 修改訂單服務(wù)
- OrderService.java
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.core.AmqpTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class OrderService { private static final Logger logger = LoggerFactory.getLogger(OrderService.class); @Autowired private AmqpTemplate amqpTemplate; @Autowired private OrderRepository orderRepository; // 創(chuàng)建訂單并發(fā)送消息到TTL隊(duì)列 public void createOrder(String orderId) { Order order = new Order(orderId, "NEW"); orderRepository.save(order); logger.info("創(chuàng)建訂單: {}", orderId); amqpTemplate.convertAndSend(RabbitMQConfig.ORDER_EXCHANGE, RabbitMQConfig.TTL_ORDER_ROUTING_KEY, orderId); } // 支付訂單 public void payOrder(String orderId) { Order order = orderRepository.findById(orderId).orElse(null); if (order != null && "NEW".equals(order.getStatus())) { order.setStatus("PAID"); orderRepository.save(order); logger.info("支付訂單: {}", orderId); } } }
2. 修改訂單超時(shí)監(jiān)聽(tīng)器
- OrderTimeoutListener.java
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class OrderTimeoutListener { private static final Logger logger = LoggerFactory.getLogger(OrderTimeoutListener.class); @Autowired private OrderRepository orderRepository; // 監(jiān)聽(tīng)來(lái)自order-queue隊(duì)列的消息 @RabbitListener(queues = RabbitMQConfig.ORDER_QUEUE) public void handleOrderTimeout(String orderId) { Order order = orderRepository.findById(orderId).orElse(null); if (order != null && "NEW".equals(order.getStatus())) { order.setStatus("CANCELLED"); orderRepository.save(order); logger.info("訂單超時(shí)未支付,取消訂單: {}", orderId); } else { logger.info("訂單已經(jīng)處理: {}", orderId); } } }
3. 訂單實(shí)體和倉(cāng)庫(kù)
- Order.java
import javax.persistence.Entity; import javax.persistence.Id; @Entity public class Order { @Id private String id; private String status; // getters and setters public Order() { } public Order(String id, String status) { this.id = id; this.status = status; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } }
- OrderRepository.java
import org.springframework.data.repository.CrudRepository; public interface OrderRepository extends CrudRepository<Order, String> { }
方法二:使用消息確認(rèn)機(jī)制(ACK/NACK)
1. 配置消息監(jiān)聽(tīng)器為手動(dòng)確認(rèn)模式
- RabbitMQConfig.java
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitMQConfig { // 其他配置... @Bean public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) { SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); factory.setConnectionFactory(connectionFactory); factory.setAcknowledgeMode(AcknowledgeMode.MANUAL); // 設(shè)置手動(dòng)確認(rèn) return factory; } }
2. 修改訂單超時(shí)監(jiān)聽(tīng)器以手動(dòng)確認(rèn)消息
- OrderTimeoutListener.java
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.rabbitmq.client.Channel; @Component public class OrderTimeoutListener implements ChannelAwareMessageListener { private static final Logger logger = LoggerFactory.getLogger(OrderTimeoutListener.class); @Autowired private OrderRepository orderRepository; @Override @RabbitListener(queues = RabbitMQConfig.ORDER_QUEUE) public void onMessage(Message message, Channel channel) throws Exception { String orderId = new String(message.getBody()); Order order = orderRepository.findById(orderId).orElse(null); if (order != null && "NEW".equals(order.getStatus())) { order.setStatus("CANCELLED"); orderRepository.save(order); logger.info("訂單超時(shí)未支付,取消訂單: {}", orderId); } else { logger.info("訂單已經(jīng)處理: {}", orderId); } // 手動(dòng)確認(rèn)消息 channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); } }
通過(guò)這種方式,我們可以在訂單支付成功后,通過(guò)數(shù)據(jù)庫(kù)標(biāo)記或手動(dòng)確認(rèn)機(jī)制,確保消息不會(huì)被重新發(fā)送或處理,從而防止訂單被錯(cuò)誤地取消。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Json字符串轉(zhuǎn)Java對(duì)象和List代碼實(shí)例
這篇文章主要介紹了Json字符串轉(zhuǎn)Java對(duì)象和List代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06LinkedBlockingQueue鏈?zhǔn)阶枞?duì)列的使用和原理解析
這篇文章主要介紹了LinkedBlockingQueue鏈?zhǔn)阶枞?duì)列的使用和原理解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10Java鏈表(Linked List)基本原理與實(shí)現(xiàn)方法入門示例
這篇文章主要介紹了Java鏈表(Linked List)基本原理與實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了Java鏈表(Linked List)的功能、原理、實(shí)現(xiàn)方法與操作注意事項(xiàng),需要的朋友可以參考下2020-03-03Java去重排序之Comparable與Comparator的使用及說(shuō)明
這篇文章主要介紹了Java去重排序之Comparable與Comparator的使用及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04Java獲取指定父節(jié)點(diǎn)、子節(jié)點(diǎn)的方法實(shí)現(xiàn)
在Java中,要獲取指定節(jié)點(diǎn)的父節(jié)點(diǎn)和子節(jié)點(diǎn),通常需要使用 DOM,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-02-02SpringBoot2零基礎(chǔ)到精通之JUnit 5與指標(biāo)監(jiān)控
SpringBoot是一種整合Spring技術(shù)棧的方式(或者說(shuō)是框架),同時(shí)也是簡(jiǎn)化Spring的一種快速開(kāi)發(fā)的腳手架,本篇讓我們一起學(xué)習(xí)JUnit 5與指標(biāo)監(jiān)控2022-03-03Spring Cloud下基于OAUTH2認(rèn)證授權(quán)的實(shí)現(xiàn)示例
這篇文章主要介紹了Spring Cloud下基于OAUTH2認(rèn)證授權(quán)的實(shí)現(xiàn)示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03SSM框架下實(shí)現(xiàn)登錄注冊(cè)的示例代碼
這篇文章主要介紹了SSM框架下實(shí)現(xiàn)登錄注冊(cè)的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06使用React和Java實(shí)現(xiàn)文本摘要小工具
本文將詳細(xì)介紹如何使用 React 和 Java 搭建一個(gè)小型文本摘要工具,并基于 Hugging Face 提供的 API 來(lái)實(shí)現(xiàn)智能摘要功能,感興趣的可以了解下2024-11-11