#概述
我们在业务开发中经常会遇到类似的需求: 10分钟未付款的订单需要自动取消掉,30分钟没有活动的用户需要注销掉等。通常的解决方法有以下两种:
轮询处理
1,构建一个集合。
2,新的订单过来时,放入该集合。
3,启动一个定时器,每分钟扫描一次该集合处理符合条件的订单。
多定时器回调
1,新的订单过来时,设置一个10分钟的定时器。
2,定时器触发时,处理该订单是否要取消。
方案一:只启动一个定时器,但是需要扫描整个集合。依赖于集合的大小,如果集合过大,则业务处理延时非常大。并且每次都要扫描整个集合,造成很多重复的运算检查。效率非常低下。
方案二:需要启动多个定时器,当任务量过大的时候,会把内存撑爆。
#环形任务槽处理
通过建立多个任务槽,每个槽只存储一部分订单id。启动一个线程,处理当前时间需要处理的槽的数据。新的订单过来时,放入当前时间对应槽的前一个。
1,我们定义10个槽。同时启动一个定时器,每分钟执行一次。
2,定时器执行时,(取当前时间分钟数)%10,得到要处理的槽,比如槽0。则处理槽0里面所有的订单,检查是否超时。
3,新的订单过来时,(取当前时间分钟数-1)%10,得到对应的槽,比如槽9,将订单id放入槽9。则10分钟后,就会处理到槽9.
实现代码如下:
测试代码如下:
在实际编程中我们需要注意,如果当前任务周期内没有把所有的任务都处理完的处理方案的选择。继续当前任务周期内任务处理就会占用下个任务周期的时间,或者未处理完的任务在下个周期再进行处理。
优点:只需要启用一个定时器,并且不需要轮询所有的订单id。
缺点:如果要执行的任务周期比较长时,并且任务时间窗口比较小时,则需要开启很多的槽。
扩展
以上只是一个demo的实现,在实际业务中我们可能需要考虑使用kafka消息队列来做实现。每个topic代表一个任务槽,kafka消息队列天然具有分布式任务处理能力,当任务量很大时可以通过增加消费者的方式获取待处理任务。另外,任务的具体执行可以通过线程池或分布式任务任务调度程序执行,快速的在指定时间周期内,处理完任务。
任务周期比较长时,槽的数量会非常短,同时也使每个槽内的任务数比较少。这种情况,可以使用redis的队列处理。
领取专属 10元无门槛券
私享最新 技术干货