【任务调度系统第二篇】:XXL Job源码分析

2023-11-12

文章目录

写在前面

首先附上xxl job项目源码地址:https://github.com/xuxueli/xxl-job
本文章比较长,主要讲解思路为:

  • XXL JOB项目源码整体概括
  • xxl-job-admin的源码分析
  • 执行器代码分析
  • XXL Job需要改进的地方

一.XXL JOB项目源码整体概括

1. 源码整体概括说明

这个项目是作为工程开发的同学们很值得学习的一个开源项目。代码整体风格比较好,模块化清晰。代码逻辑遵行Web的MVC架构,采用Spring boot + Mybatis的框架组合来组织代码。
代码总体分为三部分:
一.xxl-job-core: 这是公共服务模块,比如提供RPC远程调度,线程管理等。从业务角度去分析这个模块是没有意义的,很容易一脑雾水,因为这个模块不是独立的服务,它只是为xxl-job-admin和xxl-job-executors-sample提供了功能模块。

二. xxl-job-admin: web交互的后台引擎,这里称为调度中心。主要负责下面几件事情:

  1. 负责web端交互:作为Web后台引擎,提供了登录权限管理,任务增删改查操作,执行器组管理,GLUE任务在线编辑,日志管理等
  2. 与MySQL数据库交互,把数据持久化。
  3. 提供RPC接口,供执行器注册,维持和执行器的心跳。
  4. 与quartz交互,把任务调度的事情交给quartz去做。

三. xxl-job-executors-sample。主要做以下两件事情:

  1. 执行器初始化,并且主动注册到调度中心那里去。
  2. bean的方式注入我们线下编辑好的任务。

整体架构图如下。后续章节会对细节进行展开阐述。
在这里插入图片描述

图1. 代码整体逻辑架构图

在这里插入图片描述
图2. xxl job的Web UI真实界面

2.分析该项目源码时一些必须的知识

磨刀不误砍材工,在正式深入分析这个项目之前,有些知识有必要预知下:
1.quartz的用法。
2.freemarker渲染前端界面的原理和用法。
3.java基本功,以及spring boot和mybatis相关框架知识。

2.1 quartz简单介绍

xxl job的任务调度是依赖于quartz的。 quartz可用于创建执行数十,数百甚至数十万个作业的简单或复杂的计划; 任务定义为标准Java组件的任务,可以执行任何可以对其进行编程的任何内容。我们先从quartz官网的一个例子说起:

// 第一步,定义任务类。这个class必须要实现Job接口的execute方法。
public class HelloJob implements Job {
   

    private static Logger _log = LoggerFactory.getLogger(HelloJob.class);
    public HelloJob() {
   
    }
    public void execute(JobExecutionContext context)
        throws JobExecutionException {
   
        _log.info("Hello World! - " + new Date());
    }

}

//2. 定义任务的执行逻辑,将任务和触发器绑定起来。 
public class SimpleExample {
   

  public void run() throws Exception {
   

    log.info("------- 初始化----------------------");
 
    // 首先,我们得到一个scheduler实例
    SchedulerFactory sf = new StdSchedulerFactory();
    Scheduler sched = sf.getScheduler();

    log.info("------- 初始化完成 -----------");

    // computer a time that is on the next round minute
    Date runTime = evenMinuteDate(new Date());

    log.info("------- 调度任务 -------------------");

    // define the job and tie it to our HelloJob class
    JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1").build();

    // Trigger the job to run on the next round minute
    Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1").startAt(runTime).build();
 
    // 告诉quartz利用trigger触发器来调度job
    sched.scheduleJob(job, trigger);
    log.info(job.getKey() + " will run at: " + runTime);

    // Start up the scheduler (nothing can actually run until the
    // scheduler has been started)
    sched.start();

    log.info("------- 任务已经已经启动了 -----------------");

    // wait long enough so that the scheduler as an opportunity to
    // run the job!
    log.info("------- Waiting 65 seconds... -------------");
    try {
   
      // wait 65 seconds to show job
      Thread.sleep(65L * 1000L);
      // executing...
    } catch (Exception e) {
   
      //
    }

    // shut down the scheduler
    log.info("------- 调度关闭 ---------------------");
    sched.shutdown(true);
    log.info("------- 关闭完成 -----------------");
  }
   
  public static void main(String[] args) throws Exception {
   

    SimpleExample example = new SimpleExample();
    example.run();

  }
}

从上面的demo可以看出quartz的关键API:

  • Scheduler - 进行作业调度的主要接口.
  • Job - 作业接口,编写自己的作业需要实现,如例子中的HelloJob
  • JobDetail - 作业的详细信息,除了包含作业本身,还包含一些额外的数据。
  • Trigger - 作业计划的组件-作业何时执行,执行次数,频率等。
  • JobBuilder - 建造者模式创建 JobDetail实例.
  • TriggerBuilder - 建造者模式创建 Trigger 实例.
  • QuartzSchedulerThread 继承Thread 主要的执行任务线程

从上面的几个接口,可以看到quartz设计非常精妙,将作业和触发器分开设计,同时调度器完成对作业的调度。
整个执行过程可以概括如下:

  1. 从StdSchedulerFactory获取scheduler
  2. 创建JobDetail
  3. 创建Trigger
  4. scheduler.scheduleJob()将任务和触发器绑定起来

所以quartz的核心元素可以表示为如下图:
在这里插入图片描述
图3. quartz内部核心模块关系图

quartz不是以定时器的方式去执行任务的,而是通过线程池去完成。配置文件quartz.properties配置了线程池相关的参数。在quartz中,有两类线程,Scheduler调度线程和任务执行线程,其中任务执行线程通常使用一个线程池维护一组线程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bT3CYQD3-1571059534531)(https://cdn.nlark.com/yuque/0/2018/png/171710/1540885992917-894fa2a9-f022-4a35-80fb-18a84507f89c.png “”)]

图4. quartz的线程视图

调度线程主要有两个:执行常规调度的线程,和执行misfiredtrigger的线程。常规调度线程轮询存储的所有trigger,如果有需要触发的trigger,即到达了下一次触发的时间,则从任务执行线程池获取一个空闲线程,执行与该trigger关联的任务。Misfire线程是扫描所有的trigger,查看是否有misfiredtrigger,如果有的话根据misfire的策略分别处理(fire now 或者 wait for the next fire)。

quartz内部的数据是存入数据库的,总共有12张表。Quartz集群中,独立的Quartz节点并不与另一其的节点或是管理节点通信,而是通过相同的数据库表来感知到另一Quartz应用的。 到此,我认为quartz的核心要点应该介绍完了。

2.2 freemarker前端渲染模板简介

freemarker是一个java模板引擎。是一种基于模板和要改变的数据,并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。类似于JSP,volecity。这里不细说,有这个概念就好了。

2.3 java基本功修炼

xxl job的源码阅读,需要一定的java工程功底。特别要熟悉下spring boot, mybatis框架。

二. xxl-job-admin的源码分析

xxl-job-admin是项目的核心,称为调度中心,也是一个典型的web项目架构。通常对于一个web程序来说,我们分析时,主要是关注两件事情:第一,这个程序在初始化(也就是程序启动的时候)干了哪些事情;第二,程序的Restful接口分析,这个是Web项目最大的主线。下面的分析我们也主要是从这两点分别展开。

1. 调度中心初始化

JVM执行一个java程序时,会经历编译,加载,分配内存和执行等过程。spring boot采用了的bean方式初始化了一些对象,这些对象包括了数据库连接池,前端界面渲染的引擎,配置文件读取,quartz调度引擎,拦截器等等,这些对象一旦初始化,就会从JVM的方法区里实例化到堆内存里面去,可以供进程后续的调用。这里有个和我们业务直接相关的bean初始化,代码如下:

<!--classpath:applicationcontext-xxl-job-admin-xml-->
<bean id="xxlJobDynamicScheduler" class="com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler" init-method="init" destory-method="destory">
   <property name="scheduler" ref="quartzScheduler" />
   <property name="accessToken" value="${xxl.job.accessToken" />
</bean>

这个XxlJobDynamicScheduler类在初始化化时,执行了init方法。我们来重点分析下这个init方法干了哪些事情。

public void init() throws Exception {
   
  // 1. 调度中心注册守护线程,就是一直守护着执行器的注册,维持着和执行器之间的心跳
  JobRegistryMonitor.getInstance.start();
  
  // 2. 任务失败处理的守护线程
  JobFailMonitorHelper.getInstance().start();
  // 3. 初始化本地调度中心服务
  NetComServerFactory.putService(AdminBiz.class, XxlJobDynamicScheduler.adminBiz);
  NetComServerFactory.setAcessToken(accessToken);
  
  // 4.国际化 
  initI18n();
  Assert.notNull(scheduler, "quartz scheduler is null");
  logger.info(">>>>>>> init xxl-job admin success");
}
  1. JobRegistryMonitor.getInstance.start()是开启了一个单独的线程,这个线程每30s去轮训一下数据库。如果某个执行器的注册信号(也叫作心跳)在近90s内没有写入
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【任务调度系统第二篇】:XXL Job源码分析 的相关文章

随机推荐

  • 微信小程序——数据绑定

    在微信小程序中 可以通过以下代码实现数据绑定 在WXML中 使用双大括号 绑定数据 将数据渲染到对应的视图中
  • 【蓝桥杯】 (算法训练篇)K好数

    蓝桥杯 算法训练篇 K好数 问题描述 如果一个自然数N的K进制表示中任意的相邻的两位都不是相邻的数字 那么我们就说这个数是K好数 求L位K进制数中K好数的数目 例如K 4 L 2的时候 所有K好数为11 13 20 22 30 31 33
  • 一个人,仅30天!开发一款3D竞技足球游戏!他究竟经历了些什么?

    今天 晓衡向大家推荐一款Coco Store 优质 3D足球竞技游戏 资源 足球快斗 玩法介绍 游戏为 7V7 足球竞技类玩法 玩家控制本队的一个球员 脚下高亮圆圈显示的是玩家 其他球员和守门员为电脑AI控制 期间可以玩家可以换人 A按钮换
  • pycharm tips pycharm 使用技巧

    你好 这里只是pycharm tips 的翻译内容 任何工具窗口中的Esc键都将焦点移动到编辑器 Shift Esc将焦点移动到编辑器 并隐藏当前 或最后一个活动 工具窗口 F12键将焦点从编辑器移动到最后一个聚焦工具窗口 在编辑器中Ctr
  • Python语言程序设计 第一周习题

    Python语言程序设计 第一周习题 习题1 获得用户输入的一个整数 参考该整数值 打印输出 Hello World 要求 如果输入值是0 直接输出 Hello World 如果输入值大于0 以两个字符一行方式输出 Hello W
  • ESP32使用Arduino发布和订阅MQTT

    该项目展示了如何在ESP32上使用MQTT通信协议来发布消息和订阅主题 举例来说 我们会将BME280传感器的读数发布到Node RED仪表板 并控制ESP32输出 我们将使用Arduino IDE对ESP32进行编程 介绍 在此示例中 有
  • Ubuntu-16.06 + OpenCV-3.3.0 + 一些常见的问题

    前言 2017 11 18 根据实践过程整理出第一版 2018 01 05 更新对 pkgconfig 的说明 今天在跑一个faster rcnn的案例 https pjreddie com darknet yolo 涉及到编译 openC
  • tomcat 虚拟目录和目录浏览设置

    为了方便开发 需要开设置虚拟目录和目录浏览 如果是生产环境就不要开放目录浏览 设置虚拟目录 先建 d site 设置 CATALINA HOME bin conf server conf
  • Android--使用相对布局搭建用户注册界面

  • oracle:any,some,all的区别

    相关例子 SELECT emp empno emp ename emp job emp sal FROM scott emp WHERE sal gt any SELECT sal FROM scott emp WHERE job MANA
  • Spring 过滤器 拦截器 AOP区别

    简介 这几天在回顾Spring的AOP时 对过滤器 拦截器 AOP的关系有点好奇 故记录做以备份 在实现一些公共逻辑的时候 很多功能通过过滤器 拦截器 AOP都能实现 但是不同的方式有不同的效率 具体有什么区别 看下文描述 前后端交互基本逻
  • 使用Java VisualVM监控远程JVM(远程服务器为linux配置)

    我们经常需要对我们的开发的软件做各种测试 软件对系统资源的使用情况更是不可少 目前有多个监控工具 相比JProfiler对系统资源尤其是内存的消耗是非常庞大 JDK1 6开始自带的VisualVM就是不错的监控工具 这个工具就在JAVA H
  • C++ 函数重载(overroad) 覆盖(override) 隐藏(hide) 的区别

    原文转自 http blog chinaunix net u 15921 showart 227111 html 成员函数被重载的特征 1 相同的范围 在同一个类中 2 函数名字相同 3 参数不同 4 virtual 关键字可有可无 覆盖是
  • 点到UI 不会误点到物体

    这几天在做捕鱼达人游戏时发现 当鼠标点击UI时 炮台的子弹也会发射子弹 这样会影响用户体验 然后网上百度了一波 发现在UGUI系统上 EventSystem提供了一些方法 那就是EventSystem current IsPointerOv
  • ZeroMQ入门

    官网 ZeroMQ 简介 ZeroMQ是一个库 不是消息队列也不是消息中间件 介于应用层和传输层之间 按照TCP IP划分 传统的Socket通信模式需要创建连接 销毁连接 选择协议等一些列操作 而ZeroMQ是在Socket封装一层的并行
  • Java使用多线程查询数据

    目录 前言 实例 前言 什么是进程 进程 是操作系统的概念 一个独立运行的程序 就是一个 进程 什么是线程 线程 是由 进程创建 的 一个进程可以创建任意多的线程 每个线程都包含一些代码 线程中的代码会同主进程或者其他线程 同时运行 什么是
  • 排序算法(六)——希尔排序

    基本思想 希尔排序是基于插入排序的 又叫缩小增量排序 在插入排序中 标记符左边的元素是有序的 右边的是没有排过序的 这个算法取出标记符所指向的数据 存入一个临时变量 接着 在左边有序的数组中找到临时变量应该插入的位置 然后将插入位置之后的元
  • android接入opencv方式

    原网址 https www cnblogs com xiaoxiaoqingyi p 6676096 html Android 接入 OpenCV库的三种方式 OpenCV是一个基于BSD许可 开源 发行的跨平台计算机视觉库 可以运行在Li
  • 按键控制数码管加减清零

    实验说明 实验接线 独立按键模块 gt 单片机管脚 K1 gt P31 K2 gt P30 K3 gt P32 K4 gt P33 未使用 大家可以自己扩展功能 动态数码管模块 gt 单片机管脚 参考动态数码管实验接线 开发攻略内在对应的实
  • 【任务调度系统第二篇】:XXL Job源码分析

    文章目录 写在前面 一 XXL JOB项目源码整体概括 1 源码整体概括说明 2 分析该项目源码时一些必须的知识 2 1 quartz简单介绍 2 2 freemarker前端渲染模板简介 2 3 java基本功修炼 二 xxl job a