**1.**概述
1.1****什么是任务调度
我们可以思考一下业务场景的解决方案:
某电商平台需要每天上午10点,下午3点,晚上8点发放一批优惠卷
某银行系统需要在信用卡到期还款日的前三天进行短信提醒
某财务系统需要在每天凌晨0:10分结算前天的财务数据,统计汇总
以上场景都需要任务调度所解决

1.2****为什么需要?
感觉spring给我们提供的这个注解可以完成任务调度的功能,但是为什么要分布式的呢?
- 高可用:单机版的定时任务调度只能在一台机器上运行,如果程序或系统出现异常就会导致功能不可用。
- 防止重复执行:在单机模式下,定时任务是没什么问题的。但当我们部署了多台服务,同时又每台服务又有定时任务时,若不进行合理的控制在同一时间,只有一个定时任务启动执行,这时定时执行的结果就可能存在混乱和错误。
- 单机处理极限:原本1分钟需要处理1w订单,但是现在需要1min处理1w订单,原来一个统计需要1d,现在业务方需要10min统计,单机的CPU是有限的,多线程处理也不一定可以满足。
1.3 xxl-job****介绍
xxl-job:是大众点评的分布式任务调度平台,是一个轻量级分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展
大众点评目前已经接入xxl-job,该系统在内部已调度100w次,表现优异。
系统架构图

**2.**快速入门
2.1****简单配置
- 先下载源码,上csdn查
- 在项目的doc文件可以查看项目的架构等信息,先执行sql脚本

- 修改xxl-job-admin的application文件
修改连接的数据库

修改定时任务执行时,任务失败发送邮件、短信,可扩展钉钉

访问路径

设置好了启动项目,项目的默认账号密码是:admin/123456
2.2****配置部署执行器项目
2.2.1添加maven****依赖
XML
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- xxl-job-core -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.4.0-SNAPSHOT</version>
</dependency>
注意:xxl-job-core的版本要看上面下载的项目的版本号
2.2.2编写配置和config
XML
# web port
server.port=8081
### 调度中心部署跟地址[选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址
进行"执行器心跳注册"和"任务结来回调",为空则关闭
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
#### 执行器通讯TOKEN[选填]:非空时启用
xxl. job.accessToken=default token
####执行器AppName[选填]:执行器心跳注册分组依据:为空则关闭自动注册
xxl.job.executor.appname=xxl-job-executor-sample
### 执行器注册[选填]:优先使用该配置作为注册地址,为空时使用内服务IP:PORT "作为注册地址。从而
更灵活的支持容器类型执行器动态IP和动态映射
xxl.job.executor.address=
#### 执行器IP [选填]:默认为空表示自动获取1P,多网卡时可手动设置指定1P,该1P不会绑定Host仅作
为通讯实用;地址信息用于"执行器注册"和"调度中心请
xxl.job.executor.ip=127.0.0.1
#### 执行器端口号[选填]:小于等于0则自动获取:默认端口为9999,单机部署多个执行器时,注意要配置不
同执行器端口;
xxl.job.executor.port=9999
#### 执行器运行日志文件存储磁盘路径[选填]:需要对该路径拥有读写权限:为空则使用默认路径
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
#### 执行器日志文件保存天数[选填]:过期日志自动清理,限制值大于等于3时生效,否则,如-1,关闭自
动清理功能
xxl.job.executor.logretentiondays=30
java
package com.example.xxljobdemo.config;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* xxl-job config
*
* @author xuxueli 2017-04-28
*/
@Configuration
public class XxlJobConfig {
private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
@Value("${xxl.job.admin.addresses}")
private String adminAddresses;
@Value("${xxl.job.accessToken}")
private String accessToken;
@Value("${xxl.job.executor.appname}")
private String appname;
@Value("${xxl.job.executor.address}")
private String address;
@Value("${xxl.job.executor.ip}")
private String ip;
@Value("${xxl.job.executor.port}")
private int port;
@Value("${xxl.job.executor.logpath}")
private String logPath;
@Value("${xxl.job.executor.logretentiondays}")
private int logRetentionDays;
@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
logger.info(">>>>>>>>>>> xxl-job config init.");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppname(appname);
xxlJobSpringExecutor.setAddress(address);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
return xxlJobSpringExecutor;
}
/**
* 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils"
组件灵活定制注册IP;
*
* 1、引入依赖:
* <dependency>
* <groupId>org.springframework.cloud</groupId>
启动项目我们可以看到
2.2.3 编写定时任务
* <artifactId>spring-cloud-commons</artifactId>
* <version>${version}</version>
* </dependency>
*
* 2、配置文件,或者容器启动变量
* spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.'
*
* 3、获取IP
* String ip_ =
inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
*/
}
启动项目我们可以看到

2.2.3****编写定时任务
java
package com.example.xxljobdemo.job;
import com.xxl.job.core.handler.annotation.XxlJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class SampleXxlJob {
private static Logger logger = LoggerFactory.getLogger(SampleXxlJob.class);
@XxlJob("demoJobHandler")
public void demoJobHandler() {
System.out.println("执行定时任务,执行时间" + new Date());
}
}

在操作中启动任务


2.3 GLUE模式(java)
任务以源码方式维护在调度中心,支持通过web ide在线更新,实时编译和生效,因此不需要指定
jobHandler。
运行模式的任务实际上是一段继承自IjobHandler的java类代码,它在执行器项目中运行,可使用
@Resource/@Autowired注入执行器里的其他服务
添加****service

新增任务





改使用方法2,注意可以回滚版本

2.6****执行器集群
2.6.1****集群环境搭建
在idea设置springboot项目运行并开启多个集群


查看效果的话,需要根据任务编辑里面的路由策略:
- 第一个:固定选择第一个执行器
- 最后一个:固定选择最后一个执行器
- 轮询:依次选择在线的执行器发起调度
- 随机:随机选择在线的执行器一致性hash:每个任务按照hash算法固定选择某一台机器,且所有任务均匀散列在不同的机器上。
- 最不经常使用:使用频率最低的机器优先被选举
- 最近最久未使用:最久未使用的机器优先被选举
- 故障转移:按照顺序依次进行心跳检测,第一个心跳检测成功的机器选定为目标执行器并发起调度
- 忙碌转移:按照顺序一次进行空闲检测,第一个空闲检测成功的机器选定为目标执行并发起调度
- 分片广播:广播触发对应集群中所有机器执行一次任务,同时系统自动传递分片参数,可根据分片参数开发分片任务
**3.**分片功能讲解
3.1****案例需求讲解
需求:在指定节假日,需要给平台的所有用户去发送祝福的短信
初始化数据
在数据库中导入xxl_job_demo.sql 数据(用户的基本信息)
添加依赖

添加配置

添加实体类

添加mapper处理类

3.1.3 业务功能实现


重新启动项目
设置任务

3.2****分片概念理解
比如我们的案例中有2000+数据,如果不采取分片形式的话,任务只会在一台机器上执行,这样的话需要20+秒才能完成任务
如果采取分片广播的形式的话,一次任务调度将会广播触发对应集群中所有执行器执行一次任务,同时系统自动传递分片参数,可根据分片参数开发分片任务



我们将2000的数据,根据id分成奇数和偶数,给到上面两台机器进行执行



