分布式定时任务

Posted Java硬件工程师

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分布式定时任务相关的知识,希望对你有一定的参考价值。

本文引用了谷粒商城的课程

定时任务

定时任务是我们系统里面经常要用到的一些功能。如每天的支付订单要与支付宝进行对账操作、每个月定期进行财务汇总、在服务空闲时定时统计当天所有信息数据等。
定时任务有个非常流行的框架Quartz和Java原生API的Timer类。Spring框架也可以支持定时任务。

cron表达式

语法:秒 分 时 日 月 周 年(Spring 不支持)
http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html


特殊字符:

,:枚举: (cron=“7,9,23 * * * * ?”):任意时刻的 7,9,23 秒启动这个任务;
-:范围: (cron=“7-20 * * * * ?”):任意时刻的 7-20 秒之间,每秒启动一次
()* :任意;指定位置的任意时刻都可以
/:步长; (cron=“7/5 * * * * ?”):第 7 秒启动,每 5 秒一次;(cron=“/5 * * * * ?“):任意秒启动,每 5 秒一次;
?:(出现在日和周几的位置):为了防止日和周冲突,在周和日上如果要写通配符使用?(cron=”
* * 1 * ?”):每月的 1 号,启动这个任务;
L:(出现在日和周的位置)”, last:最后一个 (cron=“* * * ? * 3L”):每月的最后一个周二
W:Work Day:工作日 (cron=“* * * W * ?”):每个月的工作日触发 (cron=“* * * LW * ?”):每个月的最后一个工作日触发
#:第几个 (cron=“* * * ? * 5#2”):每个月的第 2 个周 4

注意:日和周几通常会发生冲突,如果日定了,那么周就要使用?通配符

可以在线生成cron表达式
https://cron.qqe2.com/

cron实例

SpringBoot整合

 * 1、Spring中6位组成,不允许第7位的年
 * 2、在周几的位置,1-7代表周一到周日:MON-SUN

测试代码如下

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;


/**
 * 定时任务
 *         1、@EnableScheduling开启定时任务
 *         2、@scheduled 开启一个定时任务
 * **/
@EnableScheduling
@Component
@Slf4j
public class HelloSchedule 
    @Scheduled(cron = "*/5 * * * * 4")
    public void hello()
        log.info("hello.....");

        try 
            Thread.sleep(8000);
         catch (InterruptedException e) 
            throw new RuntimeException(e);
        
    

但是这里存在一个问题

定时任务不应该阻塞,默认是阻塞的。(也就是要等业务执行完,定时任务才能开始执行)

可以使用以下几种解决方案
1)、CompletableFuture.runAsync()可以让业务以异步的方式,自己提交到线程池
2)、支持定时任务线程池设置:

spring.task.scheduling.pool.size=5

3)、让定时任务异步执行
通常是采用第3种方案
开启异步定时任务

  • 异步任务
    1、@EnableAsycn:开启异步任务功能
    2、@Async 给希望异步执行的方法上标注上注解
    3、自动配置类 TaskExecutionAutoConfiguration 属性绑定在TaskExecutionProperties
    4、.配置定时任务的线程池属性:
spring.task.execution.pool.core-size=5
spring.task.execution.pool.max-size=30
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;


/**
 * 定时任务
 *         1、@EnableScheduling开启定时任务
 *         2、@scheduled 开启一个定时任务
 * 异步任务
 *         1、@EnableAsycn:开启异步任务功能
 *         2、@Async 给希望异步执行的方法上标注上注解
 *         3、自动配置类 TaskExecutionAutoConfiguration 属性绑定在TaskExecutionProperties
 *         4.配置:
 *              spring.task.execution.pool.core-size=5
 *              spring.task.execution.pool.max-size=30
 * **/
@EnableScheduling
@Component
@Slf4j
@EnableAsync
public class HelloSchedule 

    /**
     * 1、Spring中6位组成,不允许第7位的年
     * 2、在周几的位置,1-7代表周一到周日:MON-SUN
     * 3、定时任务不应该阻塞,默认是阻塞的。(也就是要等业务执行完,定时任务才能开始执行)
     *      1)、CompletableFuture.runAsync()可以让业务以异步的方式,自己提交到线程池
     *      2)、支持定时任务线程池设置:
     *          spring.task.scheduling.pool.size=5
     *      3)、让定时任务异步执行
     *
     *     解决:使用异步+定时任务来完成定时任务不阻塞的功能。
     * **/
    @Async
    @Scheduled(cron = "*/5 * * * * 4")
    public void hello()
        log.info("hello.....");

        try 
            Thread.sleep(8000);
         catch (InterruptedException e) 
            throw new RuntimeException(e);
        
    

分布式定时任务设计及其框架

在很多应用系统中,我们常常要定时或周期性执行一些任务。比如,订单系统的超时状态判断、缓存数据的定时更新、定时给用户发邮件,甚至是一些定期计算的报表等。单机程序中常见的处理方式有线程的while(true) 和sleep组合,使用定时器触发任务。

1、为什么需要分布式定时任务
  • 高可用:单机版的定时任务调度只能在一台机器上运行,如果程序或者系统出现异常,就会导致功能不可用。
  • 多任务管理复杂:一个系统可能会有很多需要定时执行的任务。当出现单机无法承载所有的任务时,一般会简单地进行拆分,让不同的机器各自承担一定数量的任务。在这种方式下,需要由开发人员人工管理和分配各个机器上所负责运行的任务。并且可能因为人工分配的不合理而造成系统负载不均。
  • 单机处理极限:原本1min内需要处理1万个订单,但是现在需要1min内处理10万个订单;原来一个统计需要1h,现在业务方需要10min就统计出来。虽然我们也可以采用多线程、单机多进程处理等方式提高单位时间的处理效率,但是单机能力毕竟有限(主要是CPU、内存和磁盘),始终会有单机处理不过来的情况。
2、分布式定时任务设计思路

分布式定时任务是把分散的、可靠性差的计划任务纳入统一的平台,并实现集群管理调度和分布式部署的一种定时任务的管理方式。

核心设计思路基本都是将“调度”和“任务”两部分解耦。任务节点是分布式部署的,通过特定的均衡调度算法触发指定节点上的任务执行,如果节点任务运行异常就会被自动调度到其他节点重试,以提高系统整体稳定性和可扩展性。

  • 调度模块(调度中心): 负责管理调度信息,按照调度配置发出调度请求,自身不承担业务执行。调度系统与任务解耦,提高了系统可用性和稳定性,同时调度系统性能不再受限于任务模块;支持可视化、简单且动态的调度信息管理,包括任务新建、更新、删除、调度运行和任务告警等。所有上述操作都会实时生效,同时支持监控调度结果以及执行日志,支持执行器故障转移。
  • 执行模块(执行器):负责接收调度请求并执行任务的业务逻辑。任务模块专注于任务的执行等操作,开发和维护更加简单和高效;任务一般是“无状态”的,在任何一个节点运行都可以。执行模块接收调度中心的执行请求、终止请求和日志请求等。
3、分布式定时任务框架
  • Quartz是Java领域著名的开源任务调度工具,是开源组织OpenSymphony在Jobscheduling领域的一个开源项目。Quartz完全由Java编写而成,可以很方便地和Java的另一个框架Spring集成。支持丰富多样的调度方法,可以满足各种常规及特殊需求。支持任务和调度的多种组合方式,支持调度数据的多种存储方式。支持分布式和集群能力,负载均衡和高可用性。
  • Elastic-job是当当网开发的弹性分布式任务调度系统,功能丰富强大。由两个相互独立的子项目Elastic-Job-Lite和Elastic-Job-Cloud组成。Elastic-Job-Lite定位为轻量级无中心化解决方案,使用jar包的形式提供分布式任务的协调服务;Elastic-Job-Cloud采用自研Mesos Framework的解决方案,额外提供资源治理、应用分发以及进程隔离等功能。Elastic-job采用去中心化设计,主要分为注册中心、数据分片、分布式协调、定时任务处理和定制化流程型任务等模块。
  • TBSchedule是淘宝早期开源的分布式调度框架,基于ZooKeeper的纯Java实现。其目的是让一种批量任务或者不断变化的任务,能够被动态地分配到多个主机的JVM中的不同线程组中并行执行。所有的任务能够被不重复、无遗漏地快速处理。这种框架任务的分配通过分片实现了无重复调度,又通过架构中Leader的选择,存活的自我保证,提供了可用性和可伸缩性的保障。
  • SchedulerX是阿里云基于 Akka 架构自研的新一代功能强大、成熟稳定的分布式任务调度平台。SchedulerX为用户提供各种各样精确到秒级的高可用的任务调度服务,每日精准触发调度万亿次,允许用户配置任意周期性调度的单机或者分布式任务,提供精准、高可靠的定时任务触发,上百万超大规模任务高效并发处理和均衡调度。

以上是关于分布式定时任务的主要内容,如果未能解决你的问题,请参考以下文章

Spring专题「开发指南」手把手教你将@Schedule任务调度升级为分布式调度@DistributeSchedule

深入浅出Spring原理及实战「开发实战系列」手把手教你将@Schedule任务调度升级为分布式调度@DistributeSchedule

Spring+Quartz框架实现定时任务(集群,分布式)

spring cloud互联网分布式微服务云平台规划分析--spring cloud定时调度平台

spring cloud互联网分布式微服务云平台规划分析--spring cloud定时调度平台

Spring调度定时任务的方式