一.java的集中式任务调度
- while(true) + Thread.sleep
轮询 + 线程休眠的方式实现定时任务 - java.util.Timer + java.util.TimerTask
Timer是一种定时器工具,用于使用后台线程计划执行指定任务,它可以指定执行一个任务一次或多次
TimerTask是一个抽象类,他的一个子类代表一个可以被Timer计划的任务。 - ScheduleExecutorService
ScheduleExecutorService是从jdk1.5开始作为并发工具类被引入的,是最理想的定时任务实现方式。 - Quartz
Quartz是一个开源的定时任务调度框架,由java编写而成,用于java生态下的定时任务调度,是一个灵活方便、使用简单的定时任务调度框架,可以和Spring整合使用。 - Spring Task
Spring框架提供的轻量级的定时任务调度工具,使用方便 - Spring Boot注解@EnableScheduling + @Scheduled
底层依然采用Spring Task
集中式调度的问题:
分布式集群的模式下,如果采用集中式的任务调度方式,会带来一些问题,比如:
- 多台机器的集群部署的定时任务如何保证不被重复执行?
- 如何不重新部署动态的调整定时任务的执行实践?
- 部署定时任务的机器发生故障如何实现故障转移?
- 如何对定时任务进行监控?
- 等等…
解决方法:
- 使用数据库唯一约束
- 使用配置文件、redis、mysql作为调度的开关
- 使用分布式锁来实现调度的控制
- 使用分布式任务调度平台TBSchedule、Elastric-job、Saturn、xxl-job、Google Cron系统
二.分布式任务调度平台xxl-job
XXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。
github地址:https://github.com/xuxueli/xxl-job/
xxl-job整体架构:
1.xxl-job源码结构介绍
下载xxl-job源码并解压后,用idea打开
可以发现整个项目有
- xxl-job-admin:调度中心
- xxl-job-core:所需要的工具类,admin依赖于core项目
- xxl-job-executor-samples:使用案例
三个子项目
2.xxl-job分布式任务调度平台实践
调度中心创建:
- 从github上获取源码
- 从源码中得到SQL脚本创建和初始化数据库
- Maven编译打包xxl-job-admin并部署为调度中心
- 启动运行xxl-job-admin,并访问http://localhost:8080/xxl-job-admin
执行器向调度中心注册:
- 创建执行器(具体调度地址),可以支持集群
- 配置文件需要填写xxl-job注册中心地址,支持集群,用","分割
- 每个具体执行的job服务器都需要创建一个netty连接端口号
- 需要执行的任务方法,使用@XxlJob注解进行标注
- 在项目启动时,执行器会通过“@XxlJob”识别Spring容器中“Bean模式任务”,以注解的value属性为key管理起来。
- 方法中编写具体的job任务
(1)调度中心启动
在运行之前我们需要先更改xxl-job-admin项目中的application.properties配置文件,配置自己的数据库、邮箱、token等。
xxl-job从2.2.0开始就将数据库连接池换为了springboot2.0默认的hikari
使用sh mvnw.sh clean package -DskipTests打包之后运行
1. 更改pom文件中的artifactId
2. 复制run文件
更改run/service.sh中的serviceName和targetName
[删除logback.xml文件,logback-spring.xml不需要改,但是需要在application.properties中添加LOG_HOME配置
logback和logback-spring.xml都可以用来配置logback,但是2者的加载顺序不一样
logback.xml加载早于application.properties
所以如果你在logback.xml使用了变量时,而恰好这个变量是写在application.properties时,那么就会获取不到,只要改成logback-spring.xml就可以解决。]
3. 复制mvnw.sh文件
之后构建jar包使用sh mvnw.sh clean package -DskipTests进行构建
4. 更改dockerfile文件
更改基础镜像,下载通用包
COPY jar包和运行脚本.sh文件
更改容器运行命令
5. 更改application配置文件更改,新增application-dev和application-prod文件
注意spring.profiles.active应改为spring.config.activate.on-profile,但启动命令中的不变
为dev,prod配置数据库地址,数据库名字我们也可以改,不需要建新的数据库,注意在数据库地址那里加上时区属性防止乱码
更改server.servlet.context-path=/xxl-job-admin
6. 注册中心的账号和密码请直接更改数据库或.sql文件(密码需要md5加密后插入)
PS:任务调不通可能是由于netty的问题
(2)执行器整合xxl-job
1. 项目中引入依赖
maven:
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.3.1</version>
</dependency>
gradle:
compile 'com.xuxueli:xxl-job-core:2.3.1'
2. 更改application配置文件
增加xxl-job的相关注册配置
3. 增加xxl-job配置类XxlJobConfig
4. 在要注册的任务上添加@XxlJob注解
这样才能在xxl-job-admin中看到
jar包冲突:
java.lang.NoClassDefFoundError: com/xxl/job/core/executor/impl/XxlJobSpringExecutor
将依赖改为compile方式
重要的几个配置:
xxl.job.admin.addresses:配置任务调度中心的地址。不用担心配置的是集群虚拟ip导致执行器只在一台机器上注册,我们的执行器注册信息是存放在数据库表中,这个项目只是用来定时发送调度请求的。
xxl.job.executor.ip:执行器的地址
xxl.job.executor.port:执行器的端口,这个端口和服务的端口需要分开。
服务的端口指的是外部访问通过哪个端口进行访问,而高贵的调度中心需要新的端口发送调度请求给项目。所以我们还需在容器那里暴露该端口供调度中心连接项目发送请求。
后面这两个需要自动注册的时候配,手动注册可以不用配
- XXL-JOB会为每次调度请求生成一个单独的日志文件,通过 “XxlJobHelper.log” 打印执行日志
- 这些日志文件就放在xxl.job.executor.logpath指定的目录下。
- “调度中心”查看执行日志时将会加载对应的日志文件。
三.项目整合xxl-job遇到的问题
1.执行器手动注册,调用不通
错误信息:xxl-job remoting error(no protocol: xxx/run), for url : xxx/run
解决:
no protocol表示我们地址没加连接协议,在手动注册的地址前加上"http://"
2.执行器地址加上连接协议http后仍调用不通
**错误信息:xxl-job remoting error(connect timed out), for url : **
解决:
连接执行器超时,请检查对应的服务端口是否打开
3.执行器自动注册和回调都失败,但是调用成功
错误信息:
注册失败报错信息:
xxl-job registry fail, registryParam:RegistryParam{registryGroup='EXECUTOR', registryKey='system-executor', registryValue='http://x.x.x.x:xxxx/'},
registryResult:ReturnT [code=500, msg=xxl-job remoting fail, StatusCode(307) invalid. for url : http://xxl-job-admin-test.com/api/registry, content=null]
回调失败报错信息:
xxl-job job callback fail, callbackResult:ReturnT [code=500, msg=xxl-job remoting fail, StatusCode(307) invalid. forurl : http://xxl-job-admin-test.com/api/callback, content=null]
解决:
查看日志发现注册和回调都失败了,但是调用是执行成功的
调用成功说明admin能够访问执行器,但是执行器注册失败,表示执行器访问不了admin
怀疑是xxl.job.admin.addresses配置的有问题,xxl.job.admin.addresses配置的是inner域名,改为ip:port就可以访问成功了
推测:
项目和admin属于同一网段,即使不使用域名也是可以直接用ip:port进行访问的,而公司内部的inner域名应该是只允许公司内部网网段进行访问的,因此用inner反而访问不通了
4.任务调度中心admin报错,数据库连接失效
错误信息:HikariCP - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl
解决:
数据库的默认超时时间为500s,但是xxl-job-admin中spring.datasource.hikari.max-lifetime=900000
因此数据库连接池中的连接还没断开,但已经超过数据的连接超时时间了
把spring.datasource.hikari.max-lifetime=400000即可解决