目录
- 定时任务
- 定时任务的应用场景
- 定时任务的实现方式
- 为什么要使用Xxl-job?有什么优点
- Springboot整合Xxl-job
- 1、下载源码
- 2、配置admin,构建镜像部署在Centos上
- 3、执行器服务
- 4、admin调度中心创建执行器
- 5、基于OpenJDK镜像部署Java应用程序
- 创建一个新SpringBoot工程将Xxl-job整合进去
定时任务
在我们的java应用程序中,可以设定在特定的时间点或按照一定的时间间隔自动执行的任务。
定时任务的应用场景
用户画像:大数据时代,比如淘宝我们的浏览记录都会被埋点,然后定时的在一个时间段(一般是选择凌晨)去分析给用户打标签tag,然后再进行定向推荐。
消息推送: 比如库存到期,证照合同到期,网点生命周期到期,需要做提前的消息提醒,可以设置定时任务每天去检查是否有即将到期的,然后进行消息推送。
数据报表:每天设置定时任务让系统自动生成数据报表进行数据统计。
状态同步:比如一个外卖平台或者物流平台,用户确认到货后,但是店员这边还没确定订单完成,订单状态一直为派送中,可设置定时任务每天凌晨去检查更新。
日志清除:可以每隔一个星期或者几天去检查系统日志文件的大小,如果过大进行清理或者持久化。
定时任务的实现方式
1、Java原生的Timer类
2、线程池技术中ScheduleExecutorService
3、Spring Task,通过@schedule实现
4、Quartz
5、Elastic-job
6、Xxl-job
为什么要使用Xxl-job?有什么优点?缺点?
①Timer类功能简单、仅支持简单的定时任务,缺乏任务管理和监控功能,不可靠。
②ScheduleExecutorService是通过Executors创建出来的,阿里开发手册中规定不能使用这种方式创建线程池,因为其最大线程数为无限大,使用不当会导致OOM。
③SpringTask也是功能有限,缺乏任务管理和监控功能,并且不适合分布式架构,只适合单体。
④Quarzt的话实现比较复杂,Elastic-job是基于uarzt重构的,还引入了Zookeeper,实现也比较复杂
xxl-job的优点:
①可视化管理界面,提供直观、易用的可视化管理界面,方便我们统一进行管理和监控任务。
②支持分布式任务调度。使用分布式环境,也支持集群分布式架构,可保证高可用性和可靠性。
③任务日志的记录和查看。
④简单且动态,学习简单,上手即用,可动态调整任务状态、启动/停止。
⑤任务监控和报警,提供了丰富的任务监控功能,可以实时查看任务的执行状态、执行日志和执行结果。同时,它还支持任务执行失败时的报警通知,可以通过邮件、短信等方式及时通知相关人员进行处理。
⑥社区活跃。
xxl-job缺点:
①xxl-job通常会将任务信息、执行历史记录等数据存储在数据库中。当任务数量众多且任务执行频繁时,数据库的读写压力会增大。如果数据库的性能不够优化,可能会导致数据存储和查询的延迟。
②并且调度中心是通过DB锁来保证执行任务的唯一性,如果任务较多,数据库锁竞争激烈会导致性能下降
Springboot整合Xxl-job
部署运行环境: jdk8 + centos7 + docker + docker-mysql(自行拉取镜像创建容器,比较简单)
1、下载源码
2、配置admin,构建镜像部署在Centos上
配置数据库,在Mysql中执行项目中自带的sql脚本:
修改xxl-job-admin中配置文件application.properties:
测试启动:
更改Dockerfile文件:
打包:
将Dockerfile和target文件夹全都上传到CentOS上:
构建镜像,并启动 xxl-admin调度中心:
docker build -t xxl-job-admin:1.0 .
docker run -it -d --name xxl-job-admin -p 8080:8080 admin:1.0
访问CentOS地址:8080/xxl-job-admin:
3、执行器服务
编写application.porperties文件:
# web port
server.port=8081
# no web
#spring.main.web-environment=false
# log config
logging.config=classpath:logback.xml
### xxl-job admin address list, such as "http://address" or "http://address01,http://address02"
xxl.job.admin.addresses=http://192.168.136.132:8080/xxl-job-admin
### xxl-job, access token
xxl.job.accessToken=default_token
### xxl-job executor appname
xxl.job.executor.appname=xxl-job-executor-test
### xxl-job executor registry-address: default use address to registry , otherwise use ip:port if address is null
xxl.job.executor.address=
### xxl-job executor server-info
xxl.job.executor.ip=
xxl.job.executor.port=0
### xxl-job executor log-path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### xxl-job executor log-retention-days
xxl.job.executor.logretentiondays=30
编写XxlJobConfig文件:
@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;
}
}
编写执行器任务代码:
@Component
public class SampleXxlJob {
@XxlJob("MyJobHandler")
public void demoJobHandler() throws Exception {
System.out.println(new Date());
}
}
先在本地启动项目:
4、admin调度中心创建执行器
如果是在本地跑执行器程序/业务程序,机器地址必须填电脑的IP地址,否则出现下面的错误:
正确执行:
5、基于OpenJDK镜像部署Java应用程序
我们项目开发完成之后如果要部署在CentOS中,我们使用基于OpenJDK镜像方式运行:
docker pull openjdk:8
将我们的应用程序代码打包,上传到CentOS指定目录下:
通过jdk镜像运行jar包,启动应用程序:
docker run --name job-demo -p 8081:8081 -p 9999:9999
-d -v /home/zzl/server/jars/xxl-job:/jar openjdk:8
java -jar /jar/xxl-job-executor-sample-springboot-2.4.1.jar
此时需要将admin调度中心的执行器的地址修改:
启动定时任务:
查看输出日志,定时任务成功启动:
docker logs -f --tail 20 job-demo
创建一个新SpringBoot工程将Xxl-job整合进去
只需要引入核心依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.4.1</version>
</dependency>
application.properties配置文件:
# web port
server.port=8088
# no web
#spring.main.web-environment=false
# log config
logging.config=classpath:logback.xml
### xxl-job admin address list, such as "http://address" or "http://address01,http://address02"
xxl.job.admin.addresses=http://192.168.136.132:8080/xxl-job-admin
### xxl-job, access token
xxl.job.accessToken=default_token
### xxl-job executor appname
xxl.job.executor.appname=xxl-job-executor-test
### xxl-job executor registry-address: default use address to registry , otherwise use ip:port if address is null
xxl.job.executor.address=
### xxl-job executor server-info
xxl.job.executor.ip=
xxl.job.executor.port=0
### xxl-job executor log-path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### xxl-job executor log-retention-days
xxl.job.executor.logretentiondays=30
xxl-job配置类:
package com.example.realdemo.demos.web.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;
@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;
}
}
controller层:
@RestController
public class BasicController {
@Autowired
private BasicService basicService;
@RequestMapping("/createTask")
public String createTask() {
basicService.createTask();
return "成功创建任务!";
}
}
service层:
@Service
public class BasicService {
@XxlJob("createTaskHandler")
public void createTask() {
System.out.println("这是我新创建的项目,现在是北京时间:" + new Date());
}
}
启动项目,去调度中心创建执行器:
创建任务:
启动定时任务,成功运行!!!!