java中定时器的设置

    在java后台开发中,我们常常会碰到一种需求:定时任务,比如超时取消未支付订单、定时推送通知(发送短信)、定时清理日志等等。这些需求在我们的开发中是随处可见的,但是往往对于一些特殊的需求不知道如何做,或者不知道如何设计。

    在这里我将它们分为三种:第一种是从时间点开始(已知起始时间点),到一定时长后触发(到达结束时间点)的任务,如超时自动取消;第二种是提前n时长的通知任务(定时任务);第三种是固定周期的任务,如每天的凌晨4点清理日志或数据等。

    下面我们针对这三种作具体分析。

    一、n分钟后触发(订单超时自动取消)

    需求:订单下单后,30分钟内未付款的订单将自动取消

    分析:起始时间点已知,时长设置已知,推算出来触发时间点

    解决方案:通过rabbitmq的延迟队列实现,其原理主要是通过TTL和Dead Letter Exchange实现的。具体可查看https://www.cnblogs.com/haoxinyue/p/6613706.html,在这里不再赘述。要注意的是,一个TTL队列,其中的时长必须是一致的,原因是因为消息队列的先进先出机制,即使队列top节点剩余时长是最长的,但也是它先出后,后面的才回被推出,别忽略了消息队列的本质。

    缺点:TTL时长必须一致,这样不同的时长就需要创建不同的队列

    二、提前n分钟的触发任务(定时推送)

    需求:订单约定时间点为14:00,提前10分钟通知用户

    分析:因为这里每个用户的约定时间点不一致,那么从任务开始后,任务的触发时长也不一致,没法采用方案一的解决方式。

    解决方案:采用redis的失效key监听触发。redis可以通过订阅的方式来监听失效key,以此来达到定时推送功能。通过计算可知触发时间点,再利用当前时间点和触发时间点的时间长获得失效时长times,通过redis设置任务key的失效时间(有效剩余时长)。

    缺点:不能像消息队列一样有未确认(not ack)机制,失败后可以这个任务就消失了。

    三、固定周期的触发任务(定时清理日志)

    这个就是我们经常用的cron表达式,这也是最简单的一种。网络上资料很多,就不赘述了。

 总结:至于为什么前两种场景为什么不用cron表达式通过轮询数据库的方案来解决,一是因为我们schedule定时任务的创建相比较通过redis和rabbitmq来说,内存消耗代价更大,二是因为有效场景不适合利用轮询数据库的方式,占用数据库的连接不说,订单数据一多,查询压力更大,而且做不到精确触发任务。

第一次写文章,有不足或指教的地方请留言

你可能感兴趣的:(java中定时器的设置)