我们在电商平台购物时,下单之后会有一个付款倒计时,如果在规定的时间内未付款,订单就会自动关闭 。
类似这样的场景还有很多,比如优惠劵到期失效,下单后自动发消息等 。
今天我们来讨论一下,要实现诸如此类的功能,都有哪些技术方案,这些方案的优缺点是什么 。需要说明的是,以下这些方案每一种都有其适用场景,并无绝对优劣之分 。
1,定时任务
通过定时任务关闭订单,是一种成本很低,实现也很容易的方案 。通过简单的几行代码,写一个定时任务,定期扫描数据库中的订单,如果时间过期,就将其状态更新为关闭即可 。
文章插图
优点:实现容易,成本低,基本不依赖其他组件 。
缺点:
- 时间可能不够精确 。由于定时任务扫描的间隔是固定的,所以可能造成一些订单已经过期了一段时间才被扫描到,订单关闭的时间比正常时间晚一些 。
- 增加了数据库的压力 。随着订单的数量越来越多,扫描的成本也会越来越大,执行时间也会被拉长,可能导致某些应该被关闭的订单迟迟没有被关闭 。
总结:采用定时任务的方案比较适合对时间要求不是很敏感,并且数据量不太多的业务场景 。
2,JDK延迟队列DelayQueue
文章插图
DelayQueue是JDK提供的一个无界队列,我们可以看到,DelayQueue队列中的元素需要实现Delayed,它只提供了一个方法,就是获取过期时间 。
文章插图
用户的订单生成以后,设置过期时间比如30分钟,放入定义好的DelayQueue,然后创建一个线程,在线程中通过while(true)不断的从DelayQueue中获取过期的数据 。
优点:不依赖任何第三方组件,连数据库也不需要了,实现起来也方便 。
缺点:
- 因为DelayQueue是一个无界队列,如果放入的订单过多,会造成JVM OOM 。
- DelayQueue基于JVM内存,如果JVM重启了,那所有数据就丢失了 。
总结:DelayQueue适用于数据量较小,且丢失也不影响主业务的场景,比如内部系统的一些非重要通知,就算丢失,也不会有太大影响 。
3,redis过期监听
Redis是一个高性能的KV数据库,除了用作缓存以外,其实还提供了过期监听的功能 。
在redis.conf中,配置notify-keyspace-events Ex即可开启此功能 。
【如何实现类似订单到期未支付自动关闭的功能?这6种方案任你挑选】然后在代码中继承KeyspaceEventMessageListener,实现onMessage就可以监听过期的数据量 。
public abstract class KeyspaceEventMessageListener implements MessageListener, InitializingBean, DisposableBean { private static final Topic TOPIC_ALL_KEYEVENTS = new PatternTopic("__keyevent@*"); //...省略部分代码 public void init() { if (StringUtils.hasText(keyspaceNotificationsConfigParameter)) { RedisConnection connection = listenerContainer.getConnectionFactory().getConnection(); try { Properties config = connection.getConfig("notify-keyspace-events"); if (!StringUtils.hasText(config.getProperty("notify-keyspace-events"))) { connection.setConfig("notify-keyspace-events", keyspaceNotificationsConfigParameter); } } finally { connection.close(); } } doRegister(listenerContainer); } protected void doRegister(RedisMessageListenerContainer container) { listenerContainer.addMessageListener(this, TOPIC_ALL_KEYEVENTS); } //...省略部分代码 @Override public void afterPropertiesSet() throws Exception { init(); } }
通过以上源码,我们可以发现,其本质也是注册一个listener,利用redis的发布订阅,当key过期时,发布过期消息(key)到Channel :__keyevent@*__:expired中 。
在实际的业务中,我们可以将订单的过期时间设置比如30分钟,然后放入到redis 。30分钟之后,就可以消费这个key,然后做一些业务上的后置动作,比如检查用户是否支付 。
优点:由于redis的高性能,所以我们在设置key,或者消费key时,速度上是可以保证的 。
缺点:由于redis的key过期策略原因,当一个key过期时,redis无法保证立刻将其删除,自然我们的监听事件也无法第一时间消费到这个key,所以会存在一定的延迟 。另外,在redis5.0之前,订阅发布中的消息并没有被持久化,自然也没有所谓的确认机制 。所以一旦消费消息的过程中我们的客户端发生了宕机,这条消息就彻底丢失了 。
推荐阅读
- 手机如何充值地铁卡公交卡
- 教你如何恢复手机丢失的短信,两种方法搞定
- Excel表格太大时如何打印到一张纸上?
- 怎么用PS将图片做成动态?
- SolidWorks如何造一个胶囊药丸模型?
- |领导要学会如何分辨员工
- 买了乳胶床垫,日常的清洁和保养该如何做?
- word2007如何裁剪图片?
- 笔记本电脑如何自动更换壁纸
- 如何解决“以太网没有有效的IP配置”错误?