可以思考一下下面业务场景的解决方案:
以上场景就是任务调度所需要解决的问题。
任务调度是为了自动完成特定的任务,在约定的特定时刻去执行任务的过程。
使用 Spring 中提供的注解 @Scheduled
,也能实现调度的功能,在业务类中方法上使用这个注解,然后在启动类上加上 @EnableScheduling
注解。
@Scheduled(cron = "0/20 * * * * ? ")
public void doWork(){
//doSomething
}
感觉 Spring 给我们提供的这个注解可以完成任务调度的功能,好像已经完美解决问题了,为什么还需要分布式呢?
主要有如下这几点原因:
XXL-Job:是大众点评的分布式任务调度平台,是一个轻量级分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。大众点评目前已接入XXL-JOB,该系统在内部已调度约 100 万次,表现优异。目前已有多家公司接入 xxl-job,包括比较知名的大众点评,京东,优信二手车,360金融 (360),联想集团 (联想),易信 (网易)等等。XXL-JOB 开源社区 。
xxl-job 源码项目中 /xxl-job-master/doc/XXL-JOB架构图.pptx
有各个版本的架构图记录。
xxl-job 将调度行为抽象形成调度中心公共平台,而平台自身并不承担业务逻辑,调度中心负责发起调度请求。将任务抽象成分散的JobHandler,交由执行器统一管理,执行器负责接收调度请求并执行对应的 JobHandler 中业务逻辑。因此,调度和任务两部分可以相互解耦,提高系统整体稳定性和扩展性。
源码下载地址:
解压下载的项目,用 idea 导入项目,可能需要设置 jdk、maven 等相关工具,完成后可以看到项目中有三个模块:
调度中心的运行是需要连接数据库读取相关数据的,在 /xxl-job-master/doc/db/tables_xxl_job.sql
提供了调度数据库初始化SQL脚本。下面示例在本地数据库执行脚本并成功生成相关库和表及初始化数据。
xxl-job 表介绍:
xxl_job_registry:执行器注册表,执行器在启动时会将自身信息发给调度中心,然后调度中心存入该表;
xxl_job_group:执行器信息表,维护执行器信息;包括执行器的注册名,ip地址等。与上面的 xxl_job_registry 关联使用;
xxl_job_info:调度扩展信息表, 用于保存 XXL-JOB 调度任务的扩展信息,如任务分组、任务名、机器地址、执行器、执行入参和报警邮件等等;
xxl_job_lock:任务调度锁表;
xxl_job_log:调度日志表, 用于保存 XXL-JOB 任务调度的历史信息,如调度结果、执行结果、调度入参、调度机器和执行器等等;
xxl_job_log_report:调度日志报表:用户存储 XXL-JOB 任务调度日志的报表,调度中心报表功能页面会用到;
xxl_job_logglue:任务 GLUE 日志:用于保存 GLUE 更新历史,用于支持 GLUE 的版本回溯功能;
xxl_job_user:系统用户表;
修改 xxl-job-admin
模块中的的配置文件 /xxl-job-master/xxl-job-admin/src/main/resources/application.properties
,主要把自己的数据库账号密码配置上即可。
运行 /xxl-job-master/xxl-job-admin/src/main/java/com/xxl/job/admin/XxlJobAdminApplication.java
程序即可。
Mac 和 Linux 系统可能会出现下面目录不存在问题,
这个问题一般只会出现在 Mac 和Linux 系统上,意思是无法为创建日志文件:/data/applogs/xxl-job/xxl-job-admin.log
等。
解决方案1: logback.xml 文件中的 log 路径,改成服务器中当前用户有权限访问,或真实存在的路径。
解决方案2: 在你的电脑或者服务器上手动创建 /log 目录,并授权给当前启用项目的用户
如下图在原路径前加上一个 . 表示在项目当前目录下创建日志目录。
重新运行程序后能看到 xxl-job-admin 模块在 8080 端口上启动成功,并在 xxl-job-master 项目下创建了 data 的日志目录文件夹。
此时就可以访问 xxl-job-admin 调度中心:http://localhost:8080/xxl-job-admin ,用户名和秘密为 “admin/123456” (初始化调度中心数据库时保存在 xxl_job_user 表中的用户数据,能看到如下页面表示调度中心部署成功。
File>New>Project… 新建项目,选择 Spring Initializr,选好 jdk 和项目配置相关信息如下。
执行器项目需要添加 xxl-job 的核心依赖 xxl-job-core
。
<dependency>
<groupId>com.xuxueligroupId>
<artifactId>xxl-job-coreartifactId>
<version>2.3.1version>
dependency>
在执行器项目的配置文件中 /xxl-bob-test/src/main/resources/application.properties
配置 xxl-job 的相关参数。
### 调度中心部署根地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册;
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 [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定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
注意:执行器配置的 xxl.job.accessToken 与 调度中心配置的 xxl.job.accessToken 要保持一致。
添加 xxl-job 的 配置文件 /xxl-bob-test/src/main/java/com/ganming/xxljobtest/config/XxlJobConfig.java
@Configuration
public class XxlJobConfig {
@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() {
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;}
}
添加任务处理类/xxl-bob-test/src/main/java/com/ganming/xxljobtest/job/TestXxlJob.java
,交给 Spring 容器管理,在处理方法上贴上 @XxlJob
注解。
@Component
public class TestXxlJob {
@XxlJob("testJobHandler")
public void demoJobHandler() {
LocalDateTime now = LocalDateTime.now();
String format = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println("testJobHandler 定时任务执行完毕:" + format);}
}
运行启动类 /xxl-bob-test/src/main/java/com/ganming/xxljobtest/XxlJobTestApplication.java
,会发现 8080 端口被占用,因为我们的调度中心 xxl-job-admin
正运行在 8080 端口。
在 /xxl-bob-test/src/main/resources/application.properties
文件中新增端口的修改。
server.port=8081
重新启动项目,项目启动成功后,可以在控制中心http://localhost:8080/xxl-job-admin中的执行器管理菜单里看到注册的执行器信息。
项目注册的执行器 AppName 为 2.4.3 中所配置的内容。
### 执行器 AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册
xxl.job.executor.appname=xxl-job-executor-sample
登录调度中心,在任务管理中新增任务,设置相关基本配置,注意 JobHandler 和注解中的值保持一致即可。
添加定时任务后,可以在任务管理菜单看到新增的数据,点击 操作 > 执行一次 > 弹框直接保存后可以在页面上看到执行成功提示信息,同时执行器程序中也会打印定时任务的输出内容。
直接在操作中启动定时任务,可以看到该任务状态变为运行状态,测会按照配置的 cron:0/5 * * * * ?;每隔5秒钟执行一次定时任务。
我们在新增任务时,有很多配置参数要填,如下所示:
任务以源码方式维护在调度中心,该模式的任务实际上是一段继承自 IJobHandler 的 Java 类代码并 “groovy” 源码方式维护,它在执行器项目中运行,可使用 @Resource/@Autowire 注入执行器里中的其他服务;
/xxl-bob-test/src/main/java/com/ganming/xxljobtest/service/HelloService.java
@Service
public class HelloService {
public void methodA() {
String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println("执行MethodA的方法:"+ time);
}
public void methodB() {
String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println("执行MethodB的方法:"+ time);
}
}
重启项目。
进行上述操作后,可以在任务管理菜单里看到新添加的 GLUE 模式类型任务,通过操作里的 执行一次 和 启动可以验证其功能。
通过 GLUE 模式能在调度中心控制具体执行执行器项目中的某个方法。