南宁手机平台网站,网站登录界面图片用什么软件做,世界上做的最后的网站,中国建设人才信息网官网简介#xff1a;定时任务是每个业务常见的需求#xff0c;比如每分钟扫描超时支付的订单#xff0c;每小时清理一次数据库历史数据#xff0c;每天统计前一天的数据并生成报表等等。
作者#xff1a;黄晓萌#xff08;学仁#xff09;
Java 中自带的解决方案
使用 Ti…简介定时任务是每个业务常见的需求比如每分钟扫描超时支付的订单每小时清理一次数据库历史数据每天统计前一天的数据并生成报表等等。
作者黄晓萌学仁
Java 中自带的解决方案
使用 Timer
创建 java.util.TimerTask 任务在 run 方法中实现业务逻辑。通过 java.util.Timer 进行调度支持按照固定频率执行。所有的 TimerTask 是在同一个线程中串行执行相互影响。也就是说对于同一个 Timer 里的多个 TimerTask 任务如果一个 TimerTask 任务在执行中其它 TimerTask 即使到达执行的时间也只能排队等待。如果有异常产生线程将退出整个定时任务就失败。
import java.util.Timer;
import java.util.TimerTask;public class TestTimerTask { public static void main(String[] args) {TimerTask timerTask new TimerTask() {Overridepublic void run() {System.out.println(hell world);}};Timer timer new Timer();timer.schedule(timerTask, 10, 3000);} }使用 ScheduledExecutorService
基于线程池设计的定时任务解决方案每个调度任务都会分配到线程池中的一个线程去执行解决 Timer 定时器无法并发执行的问题支持 fixedRate 和 fixedDelay。
import java.util.Timer;
import java.util.TimerTask;public class TestTimerTask { public static void main(String[] args) {TimerTask timerTask new TimerTask() {Overridepublic void run() {System.out.println(hell world);}};Timer timer new Timer();timer.schedule(timerTask, 10, 3000);} }Spring 中自带的解决方案
Springboot 中提供了一套轻量级的定时任务工具 Spring Task通过注解可以很方便的配置支持 cron 表达式、fixedRate、fixedDelay。
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class TestTimerTask {public static void main(String[] args) {ScheduledExecutorService ses Executors.newScheduledThreadPool(5);//按照固定频率执行每隔5秒跑一次ses.scheduleAtFixedRate(new Runnable() {Overridepublic void run() {System.out.println(hello fixedRate);}}, 0, 5, TimeUnit.SECONDS);//按照固定延时执行上次执行完后隔3秒再跑ses.scheduleWithFixedDelay(new Runnable() {Overridepublic void run() {System.out.println(hello fixedDelay);}}, 0, 3, TimeUnit.SECONDS);}}Spring Task 相对于上面提到的两种解决方案最大的优势就是支持 cron 表达式可以处理按照标准时间固定周期执行的业务比如每天几点几分执行。
业务幂等解决方案
现在的应用基本都是分布式部署所有机器的代码都是一样的前面介绍的 Java 和 Spring 自带的解决方案都是进程级别的每台机器在同一时间点都会执行定时任务。这样会导致需要业务幂等的定时任务业务有问题比如每月定时给用户推送消息就会推送多次。
于是很多应用很自然的就想到了使用分布式锁的解决方案。即每次定时任务执行之前先去抢锁抢到锁的执行任务抢不到锁的不执行。怎么抢锁又是五花八门比如使用 DB、zookeeper、redis。
使用 DB 或者 Zookeeper 抢锁
使用 DB 或者 Zookeeper 抢锁的架构差不多原理如下 定时时间到了在回调方法里先去抢锁。抢到锁则继续执行方法没抢到锁直接返回。执行完方法后释放锁。
示例代码如下
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;Component
EnableScheduling
public class MyTask {/*** 每分钟的第30秒跑一次*/Scheduled(cron 30 * * * * ?)public void task1() throws InterruptedException {System.out.println(hello cron);}/*** 每隔5秒跑一次*/Scheduled(fixedRate 5000)public void task2() throws InterruptedException {System.out.println(hello fixedRate);}/*** 上次跑完隔3秒再跑*/Scheduled(fixedDelay 3000)public void task3() throws InterruptedException {System.out.println(hello fixedDelay);}}当前的这个设计仔细一点的同学可以发现其实还是有可能导致任务重复执行的。比如任务执行的非常快A 这台机器抢到锁执行完任务后很快就释放锁了。B 这台机器后抢锁还是会抢到锁再执行一遍任务。
使用 redis 抢锁
使用 redis 抢锁其实架构上和 DB/zookeeper 差不多不过 redis 抢锁支持过期时间不用主动去释放锁并且可以充分利用这个过期时间解决任务执行过快释放锁导致任务重复执行的问题架构如下 示例代码如下
Component
EnableScheduling
public class MyTask {/*** 每分钟的第30秒跑一次*/Scheduled(cron 30 * * * * ?)public void task1() throws InterruptedException {String lockName task1;if (tryLock(lockName, 30)) {System.out.println(hello cron);releaseLock(lockName);} else {return;}}private boolean tryLock(String lockName, long expiredTime) {//TODOreturn true;}private void releaseLock(String lockName) {//TODO}}
看到这里可能又会有同学有问题加一个过期时间是不是还是不够严谨还是有可能任务重复执行
——的确是的如果有一台机器突然长时间的 fullgc或者之前的任务还没处理完Spring Task 和 ScheduledExecutorService 本质还是通过线程池处理任务还是有可能隔了 30 秒再去调度任务的。
使用 Quartz
Quartz[1] 是一套轻量级的任务调度框架只需要定义了 Job任务Trigger触发器和 Scheduler调度器即可实现一个定时调度能力。支持基于数据库的集群模式可以做到任务幂等执行。 Quartz 支持任务幂等执行其实理论上还是抢 DB 锁我们看下 quartz 的表结构
其中QRTZ_LOCKS 就是 Quartz 集群实现同步机制的行锁表其表结构如下
--QRTZ_LOCKS表结构
CREATE TABLE QRTZ_LOCKS (LOCK_NAME varchar(40) NOT NULL,PRIMARY KEY (LOCK_NAME)
) ENGINEInnoDB DEFAULT CHARSETutf8;--QRTZ_LOCKS记录
-----------------
| LOCK_NAME |
-----------------
| CALENDAR_ACCESS |
| JOB_ACCESS |
| MISFIRE_ACCESS |
| STATE_ACCESS |
| TRIGGER_ACCESS |
-----------------
可以看出 QRTZ_LOCKS 中有 5 条记录代表 5 把锁分别用于实现多个 Quartz Node 对 Job、Trigger、Calendar 访问的同步控制。
开源任务调度中间件
上面提到的解决方案在架构上都有一个问题那就是每次调度都需要抢锁特别是使用 DB 和 Zookeeper 抢锁性能会比较差一旦任务量增加到一定的量就会有比较明显的调度延时。还有一个痛点就是业务想要修改调度配置或者增加一个任务得修改代码重新发布应用。
于是开源社区涌现了一堆任务调度中间件通过任务调度系统进行任务的创建、修改和调度这其中国内最火的就是 XXL-JOB 和 ElasticJob。
ElasticJob
ElasticJob[2] 是一款基于 Quartz 开发依赖 Zookeeper 作为注册中心、轻量级、无中心化的分布式任务调度框架目前已经通过 Apache 开源。
ElasticJob 相对于 Quartz 来说从功能上最大的区别就是支持分片可以将一个任务分片参数分发给不同的机器执行。架构上最大的区别就是使用 Zookeeper 作为注册中心不同的任务分配给不同的节点调度不需要抢锁触发性能上比 Quartz 上强大很多架构图如下 开发上也比较简单和 springboot 结合比较好可以在配置文件定义任务如下
elasticjob:regCenter:serverLists: localhost:2181namespace: elasticjob-lite-springbootjobs:simpleJob:elasticJobClass: org.apache.shardingsphere.elasticjob.lite.example.job.SpringBootSimpleJobcron: 0/5 * * * * ?timeZone: GMT08:00shardingTotalCount: 3shardingItemParameters: 0Beijing,1Shanghai,2GuangzhouscriptJob:elasticJobType: SCRIPTcron: 0/10 * * * * ?shardingTotalCount: 3props:script.command.line: echo SCRIPT Job: manualScriptJob:elasticJobType: SCRIPTjobBootstrapBeanName: manualScriptJobBeanshardingTotalCount: 9props:script.command.line: echo Manual SCRIPT Job:
实现任务接口如下
Component
public class SpringBootShardingJob implements SimpleJob {Overridepublic void execute(ShardingContext context) {System.out.println(分片总数context.getShardingTotalCount() , 分片号context.getShardingItem() , 分片参数context.getShardingParameter());}运行结果如下
分片总数3, 分片号0, 分片参数Beijing
分片总数3, 分片号1, 分片参数Shanghai
分片总数3, 分片号2, 分片参数Guangzhou
同时ElasticJob 还提供了一个简单的 UI可以查看任务的列表同时支持修改、触发、停止、生效、失效操作 遗憾的是ElasticJob 暂不支持动态创建任务。
XXL-JOB
XXL-JOB[3] 是一个开箱即用的轻量级分布式任务调度系统其核心设计目标是开发迅速、学习简单、轻量级、易扩展在开源社区广泛流行。
XXL-JOB 是 Master-Slave 架构Master 负责任务的调度Slave 负责任务的执行架构图如下 XXL-JOB 接入也很方便不同于 ElasticJob 定义任务实现类是通过XxlJob 注解定义 JobHandler。
Component
public class SampleXxlJob {private static Logger logger LoggerFactory.getLogger(SampleXxlJob.class);/*** 1、简单任务示例Bean模式*/XxlJob(demoJobHandler)public ReturnTString demoJobHandler(String param) throws Exception {XxlJobLogger.log(XXL-JOB, Hello World.);for (int i 0; i 5; i) {XxlJobLogger.log(beat at: i);TimeUnit.SECONDS.sleep(2);}return ReturnT.SUCCESS;}/*** 2、分片广播任务*/XxlJob(shardingJobHandler)public ReturnTString shardingJobHandler(String param) throws Exception {// 分片参数ShardingUtil.ShardingVO shardingVO ShardingUtil.getShardingVo();XxlJobLogger.log(分片参数当前分片序号 {}, 总分片数 {}, shardingVO.getIndex(), shardingVO.getTotal());// 业务逻辑for (int i 0; i shardingVO.getTotal(); i) {if (i shardingVO.getIndex()) {XxlJobLogger.log(第 {} 片, 命中分片开始处理, i);} else {XxlJobLogger.log(第 {} 片, 忽略, i);}}return ReturnT.SUCCESS;}
}
XXL-JOB 相较于 ElasticJob最大的特点就是功能比较丰富可运维能力比较强不但支持控制台动态创建任务还有调度日志、运行报表等功能。 XXL-JOB 的历史记录、运行报表和调度日志都是基于数据库实现的 由此可以看出XXL-JOB 所有功能都依赖数据库且调度中心不支持分布式架构在任务量和调度量比较大的情况下会有性能瓶颈。不过如果对任务量级、高可用、监控报警、可视化等没有过高要求的话XXL-JOB 基本可以满足定时任务的需求。
企业级解决方案
开源软件只能提供基础的调度能力在监管控上的能力一般都比较弱。比如日志服务业界往往使用 ELK 解决方案短信报警需要有短信平台监控大盘现在主流的解决方案是 Prometheus等等。企业想要有这些能力不但需要额外的开发成本还需要昂贵的资源成本。
另外使用开源软件也伴随着稳定性的风险就是出了问题没人能处理想要反馈到社区等社区处理这个链路太长了早就产生故障了。
阿里云任务调度 SchedulerX[4] 是阿里巴巴自研的基于 Akka 架构的一站式任务调度平台兼容开源 XXL-JOB、ElasticJob、Quartz规划中支持 Cron 定时、一次性任务、任务编排、分布式跑批具有高可用、可视化、可运维、低延时等能力自带企业级监控大盘、日志服务、短信报警等服务。
优势
安全防护
多层次安全防护支持 HTTPS 和 VPC 访问同时还有阿里云的多层安全防护防止恶意攻击。多租户隔离机制支持多地域、命名空间和应用级别的隔离。权限管控支持控制台读写的权限管理客户端接入的鉴权。
企业级高可用
SchedulerX2.0 采用高可用架构任务多备份机制经历阿里集团多年双十一、容灾演练可以做到任意一个机房挂了任务调度都不会收到影响。
商业级报警运维
报警支持邮件、钉钉、短信、电话其他报警方式在规划中。支持任务失败、超时、无可用机器报警。报警内容可以直接看出任务失败的原因以钉钉机器人为例。运维操作原地重跑、重刷数据、标记成功、查看堆栈、停止任务、指定机器等。丰富的可视化
schedulerx 拥有丰富的可视化能力比如
用户大盘查看任务历史执行记录查看任务运行日志查看任务运行堆栈查看任务操作记录兼容开源
Schedulerx 兼容开源 XXL-JOB、ElasticJob、Quartz规划中业务不需要改一行代码即可以将任务托管在 SchedulerX 调度平台享有企业级可视化和报警的能力。 Spring 原生
SchedulerX 支持通过控制台和 API 动态创建任务也支持 Spring 声明式任务定义一份任务配置可以拿到任何环境一键启动配置如下
spring:schedulerx2:endpoint: acm.aliyun.com #请填写不同regin的endpointnamespace: 433d8b23-06e9-xxxx-xxxx-90d4d1b9a4af #region内全局唯一建议使用UUID生成namespaceName: 学仁测试appName: myTestgroupId: myTest.group #同一个命名空间下需要唯一appKey: myTest123alibaba #应用的key不要太简单注意保管好regionId: public #填写对应的regionIdaliyunAccessKey: xxxxxxx #阿里云账号的akaliyunSecretKey: xxxxxxx #阿里云账号的skalarmChannel: sms,ding #报警通道短信和钉钉jobs: simpleJob: jobModel: standaloneclassName: com.aliyun.schedulerx.example.processor.SimpleJobcron: 0/30 * * * * ? # cron表达式jobParameter: hellooverwrite: true shardingJob: jobModel: shardingclassName: ccom.aliyun.schedulerx.example.processor.ShardingJoboneTime: 2022-06-02 12:00:00 # 一次性任务表达式jobParameter: 0Beijing,1Shanghai,2Guangzhouoverwrite: truebroadcastJob: # 不填写cron和oneTime表示api任务jobModel: broadcastclassName: com.aliyun.schedulerx.example.processor.BroadcastJobjobParameter: hellooverwrite: truemapReduceJob: jobModel: mapreduceclassName: com.aliyun.schedulerx.example.processor.MapReduceJobcron: 0 * * * * ?jobParameter: 100overwrite: truealarmUsers: #报警联系人user1:userName: 张三userPhone: 12345678900user2:userName: 李四ding: https://oapi.dingtalk.com/robot/send?access_tokenxxxxx
分布式跑批
SchedulerX 提供了丰富的分布式模型可以处理各种各样的分布式业务场景。包括单机、广播、分片、MapReduce[5] 等架构如下 SchedulerX 的 MapReduce 模型简单几行代码就可以将海量任务分布式到多台机器跑批相对于大数据跑批来说具有速度快、数据安全、成本低、简单易学等特点。
任务编排
SchedulerX 通过工作流进行任务编排并且提供了一个可视化的界面操作简单拖拖拽拽即可配置一个工作流。详细的任务状态图能一目了然看到下游任务为什么没跑方便定位问题。 可抢占的任务优先级队列
常见场景是夜间离线报表业务比如很多报表任务是晚上 1、2 点开始跑要控制应用最大并发的任务数量否则业务扛不住达到并发上限的任务会在队列中等待。同时要求早上 9 点前必须把 KPI 报表跑出来可以设置 KPI 任务高优先级会抢占低优先级任务优先调度。
SchedulerX 支持可抢占的任务优先级队列可以在控制台动态配置 QA
Kubernetes 应用可以接入 SchedulerX 吗
——可以的无论是物理机、容器、还是 Kubernetes pod都可以接入 SchedulerX。
我的应用不在阿里云上可否使用 SchedulerX
——可以的任何云平台或者本地机器只要能访问公网都可以接入 SchedulerX。
原文链接
本文为阿里云原创内容未经允许不得转载。