定時輪詢
資料庫定時輪詢方式,實作想法比較簡單。啟動定時任務,每隔一定時間掃描訂單表,查詢到逾時訂單就取消。
優點:實作簡單。
缺點:輪詢時間間隔不好確定,佔用伺服器資源,影響資料庫效能。
惰性取消
當查詢訂單資訊時,先判斷該訂單是否逾時,如果逾時就先取消。
優點:實作簡單。
缺點:影響查詢以外的業務(如:統計、庫存),影響查詢效率。
JDK延遲佇列
JDK延時佇列DelayQueue是一個無界阻塞佇列,該佇列只有在延遲期滿的時候才能從中取得元素。
簡單實作程式碼demo如下,實際生產過程中會有專門的執行緒負責訊息的入隊與消費。
import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; /** * @author 向振华 * @date 2022/08/16 15:55 */ public class OrderDelayed implements Delayed { /** * 延迟时间 */ private final Long time; /** * 订单编号 */ public String orderNo; public OrderDelayed(String orderNo, long time, TimeUnit unit) { this.orderNo = orderNo; this.time = System.currentTimeMillis() + (time > 0 ? unit.toMillis(time) : 0); } @Override public long getDelay(TimeUnit unit) { return time - System.currentTimeMillis(); } @Override public int compareTo(Delayed o) { OrderDelayed orderDelayed = (OrderDelayed) o; long diff = this.time - orderDelayed.time; if (diff <= 0) { return -1; } else { return 1; } } }
import java.util.concurrent.DelayQueue; import java.util.concurrent.TimeUnit; /** * @author 向振华 * @date 2022/08/16 16:02 */ public class Test { public static void main(String[] args) { DelayQueue<OrderDelayed> delayQueue = new DelayQueue<>(); delayQueue.put(new OrderDelayed("220101001", 8, TimeUnit.SECONDS)); delayQueue.put(new OrderDelayed("220101002", 4, TimeUnit.SECONDS)); System.out.println("订单延迟队列开始执行"); while (true) { // 取队列头部元素是否过期 OrderDelayed task = delayQueue.poll(); if (task != null) { // 取消订单业务逻辑 System.out.println("订单 ---> " + task.orderNo + " 已过期准备取消"); } } } }
優點:效率高,任務觸發時間延遲低。
缺點:異常恢復困難,叢集擴展麻煩,記憶體佔用。
時間輪
時間輪演算法類似時鐘,會以某個方向按固定頻率輪動。可以用Netty的HashedWheelTimer來實現時間輪方法。
<dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.78.Final</version> </dependency>
import io.netty.util.HashedWheelTimer; import io.netty.util.Timeout; import io.netty.util.Timer; import io.netty.util.TimerTask; import java.util.concurrent.TimeUnit; /** * @author 向振华 * @date 2022/08/16 16:02 */ public class Test { public static void main(String[] args) { // 初始化时间轮 Timer timer = new HashedWheelTimer(); // 定时任务 TimerTask task1 = new TimerTask() { public void run(Timeout timeout) throws Exception { // 取消订单业务逻辑 System.out.println("订单1已过期准备取消"); } }; // 注册此定时任务(延迟时间为5秒,也就是说5秒后订单过期) timer.newTimeout(task1, 5, TimeUnit.SECONDS); // 定时任务 TimerTask task2 = new TimerTask() { public void run(Timeout timeout) throws Exception { // 取消订单业务逻辑 System.out.println("订单2已过期准备取消"); } }; // 注册此定时任务(延迟时间为3秒,也就是说3秒后订单过期) timer.newTimeout(task2, 3, TimeUnit.SECONDS); } }
優點:效率高,任務觸發時間延遲更低。
缺點:異常恢復困難,叢集擴展麻煩,記憶體佔用。
Redis過期回呼
Redis的key過期回呼事件,也能達到延遲佇列的效果。
在redis.conf加入一條設定:
notify-keyspace-events Ex
監聽設定
@Configuration public class RedisListenerConfig { @Bean RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) { RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.setConnectionFactory(connectionFactory); return container; } }
Redis過期回呼監聽方法
@Component public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener { public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) { super(listenerContainer); } @Override public void onMessage(Message message, byte[] pattern) { // 过期key,可以设置成订单号 String expiredKey = message.toString(); // 取消订单业务逻辑 System.out.println("订单 ---> " + expiredKey + " 已过期准备取消"); } }
優點:資料不易遺失,叢集擴充方便。
缺點:需要額外維護redis。
Redis有序集合
Redis的資料結構Zset,同樣可以實現延遲佇列的效果,主要利用它的score屬性,redis透過score來為集合中的成員進行從小到大的排序。透過zadd指令為佇列delayqueue 中加入元素,並設定score值表示元素過期的時間。
消費端輪詢佇列delayqueue, 將元素排序後取最小時間與目前時間比對,如小於目前時間代表已過期移除key。
public void pollOrderQueue() { while (true) { Set<Tuple> set = jedis.zrangeWithScores(delayqueue, 0, 0); String value = ((Tuple) set.toArray()[0]).getElement(); int score = (int) ((Tuple) set.toArray()[0]).getScore(); int nowSecond = System.currentTimeMillis() / 1000); if (nowSecond >= score) { jedis.zrem(delayqueue, value); System.out.println(sdf.format(new Date()) + " removed key:" + value); } if (jedis.zcard(delayqueue) <= 0) { System.out.println(sdf.format(new Date()) + " zset empty "); return; } Thread.sleep(1000); } }
優點:資料不易遺失,叢集擴充方便。
缺點:可能重複消費同一key。
任務調度
使用任務調度中間件xxl-job、ScheduleX、Elastic-Job等來實現,設定一個調度時間cron,到達訂單過期的調度時間時,觸發任務執行取消訂單業務邏輯。
例如使用xxl-job實現,訂單建立時提交一個過期任務到xxl-job伺服器,下面時執行器方法:
import com.xxl.job.core.handler.annotation.XxlJob; import org.springframework.stereotype.Component; /** * @author 向振华 * @date 2022/08/16 15:55 */ @Component public class JobHandler { @XxlJob("payExpireJobHandler") public void payExpireJobHandler(String executorParam) { // 取消订单业务逻辑 System.out.println("订单 ---> " + executorParam + " 已过期准备取消"); } }
優點:時效性強,支援分散式。
缺點:實現複雜,維護成本高。
訊息佇列
使用RocketMQ、RabbitMQ、Kafka的延時訊息,訊息在傳送到訊息佇列服務端後並不會立刻投遞,而是根據訊息中的屬性延遲固定時間後才投遞給消費者。
RocketMQ發送延時訊息的範例程式碼如下:
import com.aliyun.openservices.ons.api.Message; import com.aliyun.openservices.ons.api.ONSFactory; import com.aliyun.openservices.ons.api.Producer; import com.aliyun.openservices.ons.api.PropertyKeyConst; import com.aliyun.openservices.ons.api.SendResult; import java.util.Properties; public class Test { public static void main(String[] args) { Properties properties = new Properties(); // AccessKey ID阿里云身份验证,在阿里云RAM控制台创建。 properties.put(PropertyKeyConst.AccessKey, "XXX"); // AccessKey Secret阿里云身份验证,在阿里云RAM控制台创建。 properties.put(PropertyKeyConst.SecretKey, "XXX"); // 设置TCP接入域名,进入消息队列RocketMQ版控制台实例详情页面的接入点区域查看。 properties.put(PropertyKeyConst.NAMESRV_ADDR, "XXX"); Producer producer = ONSFactory.createProducer(properties); // 在发送消息前,必须调用start方法来启动Producer,只需调用一次即可。 producer.start(); Message msg = new Message( // 您在消息队列RocketMQ版控制台创建的Topic。 "Topic", // Message Tag,可理解为Gmail中的标签,对消息进行再归类,方便Consumer指定过滤条件在消息队列RocketMQ版服务器过滤。 "tag", // Message Body可以是任何二进制形式的数据,消息队列RocketMQ版不做任何干预,需要Producer与Consumer协商好一致的序列化和反序列化方式。 "Hello MQ".getBytes()); // 设置代表消息的业务关键属性,请尽可能全局唯一。 // 以方便您在无法正常收到消息情况下,可通过控制台查询消息并补发。 // 注意:不设置也不会影响消息正常收发。 msg.setKey("ORDERID_100"); try { // 延时消息,在指定延迟时间(当前时间之后)进行投递。最大可设置延迟40天投递,单位毫秒(ms)。 // 以下示例表示消息在3秒后投递。 long delayTime = System.currentTimeMillis() + 3000; // 设置消息需要被投递的时间。 msg.setStartDeliverTime(delayTime); SendResult sendResult = producer.send(msg); // 同步发送消息,只要不抛异常就是成功。 if (sendResult != null) { System.out.println(new Date() + " Send mq message success. Topic is:" + msg.getTopic() + " msgId is: " + sendResult.getMessageId()); } } catch (Exception e) { // 消息发送失败,需要进行重试处理,可重新发送这条消息或持久化这条数据进行补偿处理。 System.out.println(new Date() + " Send mq message failed. Topic is:" + msg.getTopic()); e.printStackTrace(); } // 在应用退出前,销毁Producer对象。 // 注意:如果不销毁也没有问题。 producer.shutdown(); } }
RocketMQ延時訊息的訂閱與普通訊息訂閱一致。
優點:高效,好擴展,支援分散式。
缺點:實現複雜,維護成本高。
以上是怎樣用Java實現自動取消未支付訂單的功能?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

Dreamweaver Mac版
視覺化網頁開發工具

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。

WebStorm Mac版
好用的JavaScript開發工具