前言


Salesforce中没有供开发者使用的线程管理,但是实际项目中我们可能需要定时的执行某些任务,这个时候我们就需要结合Schedule来实现(前一部分聊了Batch,这一部分就聊它吧...它俩不分家)。

介绍


Schedule就是在Force平台上定时运行Apex Code(包含Trigger)的一种方式。实现该功能需要实现Schedulable接口。该接口中只包含一个方法—execute(),该方法会在指定的时间进行运行,因此我们需要将我们要执行的具体Logic加入到这里。同时Schedule可以实现循环定时执行。例如AccountSchedule,我们期望在每周的周一进行执行,这也是可行的。或者只在指定的时间执行一次后不再循环执行。
  1. 调用方式

    • 页面配置:在Apex Class List界面点击Schedule Apex。输入相关信息并保存,之后平台会在符合条件的情况下执行;
    • 代码执行:通过代码的方式设置执行时间/开始时间/结束时间(笔者个人感觉页面配置的方式后台也是通过这种方式实现的)。

  2. 取消方式

    • 页面配置:在Quick Box中输入“Scheduled Jobs”进行管理;
    • 代码执行:通过代码进行执行。
需要注意的是通过 页面配置的方式最小循环时间是以天为单位的,如果需要以更小的单位(例如:小时)则必须通过代码去实现。并且代码方式可实现无终止时间的方式。


代码配置

  1. 调用方式:
    System.schedule(name, instance, firetime);
    参数说明:
    • name:String类型。代表在Scheduled Jobs管理页面中的标识;
    • instance:实现Schedulable接口的实例;
    • firetime:String类型。触发的时间。
  2. 取消方式:和Batch方式一样
    System.abortJob(ctId);
  3. 管理方式:和Batch类似,System.schedule()执行后会返回CronTrigger Id,通过CronTrigger来管理。

时间参数的说明:firetime参数实例:= '0 0 0 3 9 ? 2022'。分别代表的是:Seconds,Minutes,Hours,Day_of_Month,Month,Day_of_week,[Optional]year 对应允许的值是:

名称

特殊字符

Seconds

0-59


Minutes

0-59


Hours

0-23


Day_of_Month

1-31

, - * ? / L W

Month

1-12 或者

英文月份前三位字符

, - * /

Day_of_week

1–7 或者英文星期前三位字符

, - * ? / L #

year

null 或者1970-2099

, - * /


特殊字符解释:
  • , :代表超过一个月;
  •  -  :定义一个区间,例如JAN-SEP;
  •  *  :代表全部,例如月份是*,则代表每个月都会执行;
  • ? :不指定具体值;
  •  /  :指定增长值,例如Day_of_Month为1/5,则代表第一个月起开始,并在每个月的第五天执行;
  •  L :指定范围的结束值,例如每个月末可能为29或31或其他,这个时候采用;
  • W :指定根据具体值找出最近的一周。同时使用L和W可指定出本月的最后一周;
  • #  :指定每个月的第几周的周几(这个位置不太好叙述,周几/第几周)。#前数字代表周,#后数字代表月例如2#2,指定每月第二周运行。

实现更小时间的循环


通过了解以上的时间参数,我们发现无法直接的采用系统提供的方式进行配置时间以实现更小时间的循环。事实上,的确如此(不要喷...)。但是我们发现系统提供了取消Schedule的方式和时间定义的方式,那么我们是不是可以尝试这样去做:

  1. 定义一个Schedule,正常运行;
  2. 运行后,通过abortJob取消这个Schedule,在生成新的Schedule(改变时间参数)。
通过以上两步后(交替轮循),我们发现是可以实现的。示例:
global class Temp implements Schedulable {

    private static final String nextFireTimeTemp = '{0} {1} {2} * * ?';
    private static final Integer ADD_MINUTES = 100;
    
    global void execute(SchedulableContext SC)  {
    
        CronTrigger ct = [SELECT TimesTriggered, NextFireTime FROM CronTrigger WHERE Id=:sc.getTriggerId()];
        DateTime dt = DateTime.now().addMinutes(ADD_MINUTES);
        String[] args = new String[]{String.valueOf(dt.second()), String.valueOf(dt.minute()), String.valueOf(dt.hour())};
        String nextFireTime = String.format(nextFireTimeTemp, args);
        
        refresh(ct.id, nextFireTime);
        
    }
    
    private static void refresh(String ctId, String nextFireTime) {
        
        if(ctId != null && nextFireTime != null) {
            System.abortJob(ctId);
        } else {
            DateTime dt = DateTime.now().addMinutes(ADD_MINUTES);
            String[] args = new String[]{String.valueOf(dt.second()), String.valueOf(dt.minute()), String.valueOf(dt.hour())};
            nextFireTime = String.format(nextFireTimeTemp, args);
        }

        System.schedule('Temp', nextFireTime, instance);
    }
    
    public static void refreshNow() {
        refresh(null, null);
    }
}
这样子就可以搞定了。其中refreshNow指定初始调用(生成Schedule),refresh代表每个Schedule的生成。

限制


笔者认为比较重要的:

  • 同一时间系统只允许存在100个Schedule;
  • Schedule的定时存在推迟问题,具体运行时间根据平台和系统的资源进行调配;
  • Schedule并不支持Callout,但是由于通常和Batch配合,因此不存在这个问题;

心得


Schedule相对而言比较简单。不建议实现太小的循环时间,因为Schedule的运行时间可能推迟,太小的时间差会导致精度大打折扣。

参考


https://resources.docs.salesforce/208/latest/en-us/sfdc/pdf/salesforce_apex_language_reference.pdf

更多推荐

Salesforce Schedule