朋友们应该很多人都使用 xxl_job 作为自己的任务调度器不知道大家有没有思考一个问题xxl_job的任务是从后台配置的调度时间可以随意定义并没 整10分、整5分 这种规定也就是说每一秒都有可能有要调度的任务而任务又是持久化到数据库里的那么xxl_job是怎么实现任务都能准时调度的难道每秒查询一次数据库开始我也没搞明白查看了源码才明白了创作者思路的精妙之处做了整理跟大家分享。| 方案描述有3个很重要的类JobScheduleHelper、RingThread 和 JobTriggerPoolHelper。JobScheduleHelper定时任务助手它在最近的5秒的整数倍时间点开始执行每隔5秒钟执行一次(可以修改)查询数据库读取已过期和未来5秒要执行的任务精妙之处在这里如果该执行了就交给 JobTriggerPoolHelper 立即执行如果还没到时间则放入时间轮。RingThread时间轮处理线程它在最近的整秒点开始执行每隔1秒执行一次每次都是从时间轮里取出当前秒需要执行的任务集合交给 JobTriggerPoolHelper 立即执行。这里执行频率虽然很高但是从Map里取不查询数据库我认为这里还可以优化时间轮没有任务就把线程阻塞添加了新任务再唤醒。JobTriggerPoolHelper任务触发器它是调度任务的实际执行者收到请求立即调度。| 核心概念时间轮一个 ConcurrentHashMapInteger, ListIntegerkey是第几秒value是一个任务ID的集合。任务持久化任务存储在数据库表 xxl_job_info 里有字段 trigger_next_time下次调度时间每次执行的时候都会修改这个字段为了后面 scheduleThread 再循环的时候可以精确查询到。Nettyxxl_job分 Client 和 Dashboard 两者使用Netty进行高效交互。Netty确实非常棒很多优秀产品都使用Netty。悲观锁xxl_job的 Dashboard 可以集群部署就会出现同一时间多个节点都会查询未来5分钟要执行的job为了避免多个节点重复调度xxl_job采用了数据库悲观锁的思路对表xxl_job_lock里仅有的一条数据查询时加 for update如下图| 总结回头仔细梳理一下xxl_job的设计理念A、JobScheduleHelper、RingThread 和 JobTriggerPoolHelper三者互相配合既避免了高频访问数据库数据库压力不大还利用多线程杜绝了线程的阻塞这种巧妙的设计使得产品能稳定运行。B、利用数据库悲观锁实现了即使Dashboard集群部署任务也不会重复调用。有人会觉得这里可以用Redis来实现是可以用但为什么没有用呢我觉得是作者为了尽量让这个产品轻量化。上面两点特别是A我觉得是非常棒的思路仔细想想其实在很多地方都可以使用https://mp.weixin.qq.com/s/nqnTBcPN_rZRlyRY0CS2Fg