1.訂單失效原理
訂單失效的實現方式
1:redis的過期特性,redis提供了key過期的監聽事件接口,通過監聽key過期來實現訂單失效,不支持集群環境(主從結構存在數據副本)
2:使用rabbitMq實現延遲隊列的功能。
- 當生成訂單時,將訂單號放入死信隊列(因為沒有消息處理者,所以稱為死信隊列)設置消息的存活時間為30分鍾,
- 當30分鍾過后,死信隊列的消息會通過,路由轉發交換機,路由轉發交換機將消息發給工作隊列
- 消息處理者處理消息
2.具體實現
- 導入相應的jar包
<!--spring整合rabbitMq -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.1.2</version>
</dependency>
<!-- spring整合rabbitMq-->
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
- 配置rabbit配置文件spring-rabbit.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
<!-- 連接工廠-->
<!-- 創建連接工廠將來生產連接,工廠名為rabbitConnectionFactory,登陸名為admin,密碼為admin,連接地址為192.168.192.3 ,端口號為5672-->
<rabbit:connection-factory id="rabbitConnectionFactory" username="admin" password="admin" host="192.168.192.3" port="5672"/>
<!--以管理員身份使用上面這個連接工廠 -->
<rabbit:admin connection-factory="rabbitConnectionFactory"/>
<!-- 死信隊列,隊列名為dead_queue。自動聲明-->
<rabbit:queue name="dead_queue" auto-declare="true">
<!-- 定義隊列參數: -->
<rabbit:queue-arguments>
<!-- 設置消息的存活時間 毫秒數-->
<entry key="x-message-ttl" value="30000" value-type="java.lang.Long"/>
<!-- 引用死信路由器 -->
<entry key="x-dead-letter-exchange" value="dead_exchange"/>
<!-- 設置死信路由鍵 -->
<entry key="x-dead-letter-routing-key" value="task_queue"/>
</rabbit:queue-arguments>
</rabbit:queue>
<!-- dead_exchange死信交換機-->
<rabbit:direct-exchange name="dead_exchange" durable="false" auto-delete="false" id="dead_exchange">
<!-- 死信理由綁定轉發的隊列-->
<rabbit:bindings>
<rabbit:binding queue="task_queue"/>
</rabbit:bindings>
</rabbit:direct-exchange>
<!-- 任務隊列-->
<!-- 消費者所獲取的消息隊列-->
<rabbit:queue name="task_queue" auto-declare="true" />
<!-- 線程池-->
<!-- 消費者消費消息的任務類
線程池去調度線程去執行任務類的方法
-->
<!-- 配置線程池 -->
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<!-- 線程池維護線程的最少數量 -->
<property name="corePoolSize" value="5"/>
<!-- 線程池維護線程所允許的空閑時間 -->
<property name="keepAliveSeconds" value="30000"/>
<!-- 線程池維護線程的最大數量 -->
<property name="maxPoolSize" value="2000"/>
<!-- 線程池所使用的緩沖隊列 -->
<property name="queueCapacity" value="20"/>
</bean>
<!-- 監聽程序監聽任務隊列消費-->
<!-- 配置監聽容器-->
<rabbit:listener-container connection-factory="rabbitConnectionFactory" acknowledge="auto" task-executor="taskExecutor">
<!-- 指定監聽隊列 使用哪個監聽類, 定義了 收到消息之后的邏輯 ,來監聽-->
<rabbit:listener queues="task_queue" ref="delayTask"/>
</rabbit:listener-container>
<!-- 消息消費的任務類-->
<bean id="delayTask" class="com.oracle.shop.task.OrdersExpireMessageTask"/>
<!-- 配置生產消息的客戶端對象,將來發送消息到dead_queue -->
<!-- 實例化一個生產者客戶端對象,將來向dead_queue寫消息
spring封裝了amqpTempalte
-->
<rabbit:template id="amqpTemplate" connection-factory="rabbitConnectionFactory" queue="dead_queue" routing-key="dead_queue"/>
</beans>
- 編寫處理類
//訂單過期處理
public class OrdersExpireMessageTask implements MessageListener {
@Autowired
private OrdersService ordersService;
//消費者監聽隊列進行處理
@Override
public void onMessage(Message message) {
//消息過期后,接收並進行處理
String orderNumber = new String(message.getBody());
//查詢訂單狀態,如果未支付,則查詢支付寶接口,確認訂單狀態,然后進行操作
Orders orders = ordersService.selectOrdersByOrdersNum(orderNumber);
if (orders.getOrdersStatus() == 0) {//未支付
//通過支付包驗證訂單是否支付
try {
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID,
AlipayConfig.RSA_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET,
AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.SIGNTYPE);
//創建查詢請求
AlipayTradeQueryRequest alipayTradeQueryRequest = new AlipayTradeQueryRequest();
//設置請求參數
alipayTradeQueryRequest.setBizContent("{" + "\"out_trade_no\":\"" + orderNumber + "\"" + "}");
//執行查詢,返回查詢結果
AlipayTradeQueryResponse alipayTradeQueryResponse = alipayClient.execute(alipayTradeQueryRequest);
//對查詢結果進行處理
if (alipayTradeQueryResponse.isSuccess()) {
switch (alipayTradeQueryResponse.getTradeStatus()) // 判斷交易結果
{
case "TRADE_FINISHED": // 交易結束並不可退款,修改訂單狀態為已支付
ordersService.updateOrdersStatusPaied(orderNumber);
break;
case "TRADE_SUCCESS": // 交易支付成功,修改訂單狀態為,已支付
ordersService.updateOrdersStatusPaied(orderNumber);
break;
case "TRADE_CLOSED": // 未付款交易超時關閉或支付完成后全額退款,修改訂單狀態為,未支付
ordersService.updateOrdersStatusOverdue(orderNumber);
break;
case "WAIT_BUYER_PAY": // 交易創建並等待買家付款,修改訂單狀態為,未支付
ordersService.updateOrdersStatusOverdue(orderNumber);
break;
default:
break;
}
} else {//支付寶方面沒有該訂單,直接修改訂單信息為過期
ordersService.updateOrdersStatusOverdue(orderNumber);
}
} catch (AlipayApiException e) {
e.printStackTrace();
}
}
}
}