【xxl-job源码篇01】xxl-job源码解读 神奇的时间轮 触发流程解读

导读

xxl-job是一个分布式任务调度平台,在业内深受广大程序员的喜爱,本章将带你深入了解xxl-job的源码,理解其运行逻辑。

阅读xxl-job源码会增强你对多线程的理解与应用,调度思想的升华

本章将带你一点点剖析xxl-job设计底层逻辑,让你真正理解下图的每一个模块,让你知其然更知其所以然。

输入图片说明

项目结构

拉下代码我们首先看项目结构,xxl-job为标准的父子工程,我们看到有三个子项目

image-20220412144123632

  • xxl-job-admin 服务端
  • xxl-job-core 核心包,会被服务端和客户端同时引用
  • xxl-job-executor-samples 一些demo

源码解读——定时器

ok,现在我们需要知道xxl-job服务端启动都干了什么,首先定位到这个类XxlJobAdminConfig

可以看到xxl-job核心功能是跟随bean的生命周期运行的

@Component
public class XxlJobAdminConfig implements InitializingBean, DisposableBean {
   
    @Override
    public void afterPropertiesSet() throws Exception {
   
        adminConfig = this;
        xxlJobScheduler = new XxlJobScheduler();
      	// 初始化
        xxlJobScheduler.init();
    }
    @Override
    public void destroy() throws Exception {
   
        xxlJobScheduler.destroy();
    }
}

跟一下代码com.xxl.job.admin.core.scheduler.XxlJobScheduler#init

可以看到,此处初始化了几个核心的helper,每个helper都运用了饿汉单例模式,不会出现重复创建的情况

public void init() throws Exception {
   
    // 触发池
    JobTriggerPoolHelper.toStart();
    // 服务监听器
    JobRegistryHelper.getInstance().start();
    // 失败告警
    JobFailMonitorHelper.getInstance().start();
    // 回调监听器
    JobCompleteHelper.getInstance().start();
    // 日志回调
    JobLogReportHelper.getInstance().start();
    // 定时器
    JobScheduleHelper.getInstance().start();
}

首先定位到com.xxl.job.admin.core.thread.JobScheduleHelper#start

该helper就是定时器的心脏,其作用为定时和预触发。

启动了两个守护线程

  1. scheduleThread 任务扫描线程

    while循环,不停的扫描即将执行的任务,使用mysql for update实现排他锁,防止其他服务并行扫描,并将即将执行的任务缓存到时间轮

  2. ringThread 执行线程

    触发时间轮中的任务

时间轮

来看看时间轮这个听起来高大上的东西是如何实现的

如下,本质就是一个concurrentHashMap,key为执行的秒,value为要执行的job的id,scheduleThread线程会提前5-10秒将任务放入时间轮的list中。

private volatile static Map<Integer, List<Integer>> ringData = new ConcurrentHashMap<>();
// ringSecond 下一次执行的时间
private void pushTimeRing(int ringSecond, int jobId){
        
    // push async ring                                    
    List<Integer> ringItemData = ringData.get(ringSecond);
    if (ringItemData == null) {
                              
        ringItemData = new ArrayList<Integer>();          
        ringData.put(ringSecond, ringItemData);           
    }                                                     
    ringItemData.add(jobId);                              
}

触发时间轮中的任务

ringThread = new Thread(new Runnable() {
   
    @Override
    public void run() {
   
        while (!ringThreadToStop) {
   
            try {
   
              // 时间对其
                TimeUnit.MILLISECONDS.sleep(1000 - System.currentTimeMillis() % 1000);
            } catch (InterruptedException e) {
   
                if (!ringThreadToStop) {
   
                    logger.error(e.getMessage(), e);
                }
            }
            try {
   
                // 时间轮数据处理
                List<Integer> ringItemData = new ArrayList<>();
                int nowSecond = Calendar.getInstance().get(Calendar.SECOND);   // 避免处理耗时太长,跨过刻度,向前校验一个刻度;
                for (int i = 0; i < 2; i++) {
   
                    List<Integer> tmpData = ringData.remove( (nowSecond+60-i)%60 );
                    if (tmpData != null) {
   
                        ringItemData.addAll(tmpData);
                    }
                }
                if (ringItemData.size() > 0) {
   
                    // 触发任务
                    for (int jobId: ringItemData) {
   
                        // 触发任务
                        JobTriggerPoolHelper.trigger(jobId, TriggerTypeEnum.CRON, -1, 
  • 5
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值