【XXL-JOB】XXL-JOB的搭建和使用
文章目录
- 【XXL-JOB】XXL-JOB的搭建和使用
 - 1. 任务调度
 - 1.1 实现任务调度
 - 1.1.1 多线程实现
 - 1.1.2 Timer实现
 - 1.1.3 ScheduledExecutor实现
 
- 2. 分布式任务调度
 - 2.1 采用分布式的原因
 
- 3. XXL-JOB
 - 3.1 XXL-JOB介绍
 - 3.2 执行流程
 
- 4. 搭建XXL-JOB
 - 4.1 创建数据库
 - 4.2 导入依赖,配置执行器
 - 4.3 调度中心配置
 - 4.4 执行任务
 - 4.5 查看日志
 - 4.6 停止任务
 
1. 任务调度
任务调度:顾名思义,就是对任务的调度,它是指系统为了完成特定业务,基于给定时间点,给定时间间隔或者给定执行次数自动执行任务。
那么,我们如何去高效的处理一批任务呢?这里有两种方案:
- 多线程(多线程可以充分利用单机的资源)
 - 分布式+多线程(充分利用多台计算机资源,每台计算机使用多线程)
 
方案2的可扩展性更强,这是一种分布式任务调度的处理方案。
那么什么又是分布式任务调度呢?
给定多个场景:
- 某电商系统需要在每天上午10点,下午3点,晚上8点发放一批优惠券。
 - 某财务系统需要在每天上午10点前结算前一天的账单数据,统计汇总。
 - 某电商平台每天凌晨3点,要对订单中的无效订单进行清理。
 - 12306网站会根据车次不同,设置几个时间点分批次放票。
 - 。。。。。。
 
以上这些问题,就是任务调度需要解决的问题。
1.1 实现任务调度
1.1.1 多线程实现
我们可以开启一个线程,每隔一段时间就去执行一个任务。
如下代码所示:
这个程序的功能是每隔1s,执行一次。
public static void main(String[] args) {    
    //任务执行间隔时间
    final long timeInterval = 1000;
    Runnable runnable = new Runnable() {
        public void run() {
            while (true) {
                //TODO:something
                try {
                    Thread.sleep(timeInterval);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    };
    Thread thread = new Thread(runnable);
    thread.start();
}
 
Jdk也为我们提供了相关支持,如Timer、ScheduledExecutor。
1.1.2 Timer实现
如下代码的功能是1s之后开始调度,并且每隔2s执行一次。
public static void main(String[] args){  
    Timer timer = new Timer();  
    timer.schedule(new TimerTask(){
        @Override  
        public void run() {  
           //TODO:something
        }  
    }, 1000, 2000);  //1秒后开始调度,每2秒执行一次
}
 
Timer 的优点在于简单易用,每个Timer对应一个线程,因此可以同时启动多个Timer并行执行多个任务,同一个Timer中的任务是串行执行。
1.1.3 ScheduledExecutor实现
public static void main(String [] agrs){
    ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
    service.scheduleAtFixedRate(
            new Runnable() {
                @Override
                public void run() {
                    //TODO:something
                    System.out.println("todo something");
                }
            }, 1,
            2, TimeUnit.SECONDS);
}
 
Java 5 推出了基于线程池设计的 ScheduledExecutor,其设计思想是,每一个被调度的任务都会由线程池中一个线程去执行,因此任务是并发执行的,相互之间不会受到干扰。
2. 分布式任务调度
通常任务调度的程序是集成在应用中的,比如:优惠卷服务中包括了定时发放优惠卷的的调度程序,结算服务中包括了定期生成报表的任务调度程序,由于采用分布式架构,一个服务往往会部署多个冗余实例来运行我们的业务,在这种分布式系统环境下运行任务调度,我们称之为分布式任务调度。
如下图:

2.1 采用分布式的原因
不管是任务调度程序集成在应用程序中,还是单独构建的任务调度系统,如果采用分布式调度任务的方式就相当于将任务调度程序分布式构建,这样就可以具有分布式系统的特点,并且提高任务的调度处理能力。
所以分布式调度要实现的目标是:
- 并行任务调度: 并行任务调度实现靠多线程,如果有大量任务需要调度,此时光靠多线程就会有瓶颈了,因为一台计算机CPU的处理能力是有限的。如果将任务调度程序分布式部署,每个结点还可以部署为集群,这样就可以让多台计算机共同去完成任务调度,我们可以将任务分割为若干个分片,由不同的实例并行执行,来提高任务调度的处理效率。
 - 高可用:若某一个实例宕机,不影响其他实例来执行任务。
 - 弹性扩容:当集群中增加实例就可以提高并行任务的处理效率。
 - 任务管理与监测:对系统中存在的所有定时任务进行统一的管理及监测。让开发人员及运维人员能够时刻了解任务执行情况,从而做出快速的应急处理响应。
 - 避免任务重复执行:当任务调度以集群方式部署,同一个任务调度可能会执行多次,比如在上面提到的电商系统中到点发优惠券的例子,就会发放多次优惠券,对公司造成很多损失,所以我们需要控制相同的任务在多个运行实例上只执行一次。
 
注:第五点尤为重要。
3. XXL-JOB
3.1 XXL-JOB介绍
XXL-JOB是一个轻量级分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。
官网:XXL-JOB
文档:XXL-JOB文档
XXL-JOB主要有调度中心、执行器、任务:

- 调度中心: 
  
- 负责管理调度信息,按照调度配置发出调度请求,自身不承担业务代码
 - 主要职责为执行器管理、任务管理、监控运维、日志管理等
 
 - 执行器: 
  
- 负责接收调度请求并执行任务逻辑
 - 主要职责是注册服务、任务执行服务(接收到任务后会放入线程池中的任务队列)、执行结果上报、日志服务等
 
 - 任务: 
  
- 负责执行具体的业务处理。
 
 
3.2 执行流程
调度中心与执行器之间的工作流程如下:

执行流程:
- 任务执行器根据配置的调度中心的地址,自动注册到调度中心
 - 达到任务触发条件,调度中心下发任务
 - 执行器基于线程池执行任务,并把执行结果放入内存队列中,把执行日志写入日志文件中
 - 执行器消费内存队列中的执行结果,主动上报给调度中心。
 - 当用户在调度中心查看任务日志,调度中心请求任务执行器,任务执行器读取任务日志文件并返回日志详情。
 
4. 搭建XXL-JOB
首先下载XXL-JOB:
- GitHub:https://github.com/xuxueli/xxl-job
 - Gitee:https://gitee.com/xuxueli0323/xxl-job
 
下载后使用idea打开项目:

项目模块有三个:
- xxl-job-admin:调度中心
 - xxl-job-core:项目的核心包
 - xxl-job-executor-samples:执行器示例代码(可参考) 
  
- xxl-job-executor-sample-springboot:Springboot版本,通过Springboot管理执行器,推荐这种方式;
 - xxl-job-executor-sample-frameless:无框架版本;
 
 - doc:文档资料,包含数据库脚本
 
4.1 创建数据库
创建数据库,并导入doc文档中的数据库脚本:

导入成功之后,运行xxl-job-admin项目,登陆账号为:admin,密码为:123456
4.2 导入依赖,配置执行器
1)导入依赖
在我们需要执行任务调度的模块中导入如下依赖:
<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-job-core</artifactId>
</dependency>
 
2)yml配置
在配置文件(可以在nacos或本地文件)中配置:
xxl:
  job:
    admin: 
      addresses: http://localhost:8080/xxl-job-admin
    executor:
      appname: media-process-service #执行器名字(很重要,在调度中心要用到)
      address: 
      ip: 
      port: 9999 #执行器端口号
      logpath: /data/applogs/xxl-job/jobhandler
      logretentiondays: 30
    accessToken: default_token
 
3)配置xxl-job的执行器
将示例工程下的配置类拷贝到自己的service工程下:

4.3 调度中心配置
进入调度中心添加执行器:

点击新增,弹出如下对话框:

图中的 AppName 填我们上面配置文件中配置的执行器名字,名称随意。其他的可以不填。
填写完毕之后点击保存。

重新运行我们的项目,如果控制台中输出如下内容说明执行器在调度中心注册成功:

同时,我们可以在调度中心中查看到执行器的地址:

4.4 执行任务
我们复制示例工程中的 SampleXxjJob 到我们的项目中:

我们删除部分代码,只留如下代码(任务代码):
@Component
public class SampleXxlJob {
    private static Logger logger = LoggerFactory.getLogger(SampleXxlJob.class);
    
    /**
     * 1、简单任务示例(Bean模式)
     */
    @XxlJob("demoJobHandler")//记住这个名字,后面要使用
    public void demoJobHandler() throws Exception {
        XxlJobHelper.log("XXL-JOB, Hello World.");
        for (int i = 0; i < 5; i++) {
            XxlJobHelper.log("beat at:" + i);
            TimeUnit.SECONDS.sleep(2);
        }
        // default success
    }
}
 
1)在调度中心添加任务
进入调度中心,在任务管理中新增任务:

点击新增,填写任务信息:

注意红色标记处:
调度类型选择Cron,并配置Cron表达式设置定时策略。
Cron表达式是一个字符串,通过它可以定义调度策略,格式如下:
{秒数} {分钟} {小时} {日期} {月份} {星期} {年份(可为空)}
xxl-job提供图形界面去配置:

运行模式有BEAN和GLUE,bean模式较常用就是在项目工程中编写执行器的任务代码,GLUE是将任务代码编写在调度中心。
JobHandler任务方法名填写@XxlJob注解中的名称。
添加成功后,启动任务。

4.5 查看日志
点击左侧菜单栏“调度日志”即可查看任务执行情况:

任务执行了一段之间要注意清理日志,点击右上角的“清理”按钮即可。
4.6 停止任务
如果想要停止任务,需要在调度中心中操作:




















