日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

Spring中Cron表達式的優雅實現方案

來源: 責編: 時間:2024-03-18 09:42:48 241觀看
導讀在 SpringBoot 項目中,我們可以通過@EnableScheduling注解開啟調度任務支持,并通過@Scheduled注解快速地建立一系列定時任務。@Scheduled支持下面三種配置執行時間的方式:cron(expression):根據Cron表達式來執行。fixedDe

在 SpringBoot 項目中,我們可以通過@EnableScheduling注解開啟調度任務支持,并通過@Scheduled注解快速地建立一系列定時任務。PrK28資訊網——每日最新資訊28at.com

@Scheduled支持下面三種配置執行時間的方式:PrK28資訊網——每日最新資訊28at.com

  • cron(expression):根據Cron表達式來執行。
  • fixedDelay(period):固定間隔時間執行,無論任務執行長短,兩次任務執行的間隔總是相同的。
  • fixedRate(period):固定頻率執行,從任務啟動之后,總是在固定的時刻執行,如果因為執行時間過長,造成錯過某個時刻的執行(晚點),則任務會被立刻執行。

最常用的應該是第一種方式,基于Cron表達式的執行模式,因其相對來說更加靈活。PrK28資訊網——每日最新資訊28at.com

可變與不可變

默認情況下,@Scheduled注解標記的定時任務方法在初始化之后,是不會再發生變化的。Spring 在初始化 bean 后,通過后處理器攔截所有帶有@Scheduled注解的方法,并解析相應的的注解參數,放入相應的定時任務列表等待后續統一執行處理。到定時任務真正啟動之前,我們都有機會更改任務的執行周期等參數。PrK28資訊網——每日最新資訊28at.com

換言之,我們既可以通過application.properties配置文件配合@Value注解的方式指定任務的Cron表達式,亦可以通過CronTrigger從數據庫或者其他任意存儲中間件中加載并注冊定時任務。這是 Spring 提供給我們的可變的部分。PrK28資訊網——每日最新資訊28at.com

但是我們往往要得更多。能否在定時任務已經在執行過的情況下,去動態更改Cron表達式,甚至禁用某個定時任務呢?很遺憾,默認情況下,這是做不到的,任務一旦被注冊和執行,用于注冊的參數便被固定下來,這是不可變的部分。PrK28資訊網——每日最新資訊28at.com

創造與毀滅

既然創造之后不可變,那就毀滅之后再重建吧。于是乎,我們的思路便是,在注冊期間保留任務的關鍵信息,并通過另一個定時任務檢查配置是否發生變化,如果有變化,就把“前任”干掉,取而代之。如果沒有變化,就保持原樣。PrK28資訊網——每日最新資訊28at.com

先對任務做個簡單的抽象,方便統一的識別和管理:PrK28資訊網——每日最新資訊28at.com

public interface IPollableService {    /**     * 執行方法     */    void poll();    /**     * 獲取周期表達式     *     * @return CronExpression     */    default String getCronExpression() {        return null;    }    /**     * 獲取任務名稱     *     * @return 任務名稱     */    default String getTaskName() {        return this.getClass().getSimpleName();    }}

最重要的便是getCronExpression()方法,每個定時服務實現可以自己控制自己的表達式,變與不變,自己說了算。至于從何處獲取,怎么獲取,請諸君自行發揮了。接下來,就是實現任務的動態注冊:PrK28資訊網——每日最新資訊28at.com

@Configuration@EnableAsync@EnableSchedulingpublic class SchedulingConfiguration implements SchedulingConfigurer, ApplicationContextAware {    private static final Logger log = LoggerFactory.getLogger(SchedulingConfiguration.class);    private static ApplicationContext appCtx;    private final ConcurrentMap<String, ScheduledTask> scheduledTaskHolder = new ConcurrentHashMap<>(16);    private final ConcurrentMap<String, String> cronExpressionHolder = new ConcurrentHashMap<>(16);    private ScheduledTaskRegistrar taskRegistrar;    public static synchronized void setAppCtx(ApplicationContext appCtx) {        SchedulingConfiguration.appCtx = appCtx;    }    @Override    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {        setAppCtx(applicationContext);    }    @Override    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {        this.taskRegistrar = taskRegistrar;    }    /**     * 刷新定時任務表達式     */    public void refresh() {        Map<String, IPollableService> beanMap = appCtx.getBeansOfType(IPollableService.class);        if (beanMap.isEmpty() || taskRegistrar == null) {            return;        }        beanMap.forEach((beanName, task) -> {            String expression = task.getCronExpression();            String taskName = task.getTaskName();            if (null == expression) {                log.warn("定時任務[{}]的任務表達式未配置或配置錯誤,請檢查配置", taskName);                return;            }            // 如果策略執行時間發生了變化,則取消當前策略的任務,并重新注冊任務            boolean unmodified = scheduledTaskHolder.containsKey(beanName) && cronExpressionHolder.get(beanName).equals(expression);            if (unmodified) {                log.info("定時任務[{}]的任務表達式未發生變化,無需刷新", taskName);                return;            }            Optional.ofNullable(scheduledTaskHolder.remove(beanName)).ifPresent(existTask -> {                existTask.cancel();                cronExpressionHolder.remove(beanName);            });            if (ScheduledTaskRegistrar.CRON_DISABLED.equals(expression)) {                log.warn("定時任務[{}]的任務表達式配置為禁用,將被不會被調度執行", taskName);                return;            }            CronTask cronTask = new CronTask(task::poll, expression);            ScheduledTask scheduledTask = taskRegistrar.scheduleCronTask(cronTask);            if (scheduledTask != null) {                log.info("定時任務[{}]已加載,當前任務表達式為[{}]", taskName, expression);                scheduledTaskHolder.put(beanName, scheduledTask);                cronExpressionHolder.put(beanName, expression);            }        });    }}

重點是保存ScheduledTask對象的引用,它是控制任務啟停的關鍵。而表達式“-”則作為一個特殊的標記,用于禁用某個定時任務。PrK28資訊網——每日最新資訊28at.com

當然,禁用后的任務通過重新賦予新的 Cron 表達式,是可以“復活”的。完成了上面這些,我們還需要一個定時任務來動態監控和刷新定時任務配置:PrK28資訊網——每日最新資訊28at.com

@Componentpublic class CronTaskLoader implements ApplicationRunner {    private static final Logger log = LoggerFactory.getLogger(CronTaskLoader.class);    private final SchedulingConfiguration schedulingConfiguration;    private final AtomicBoolean appStarted = new AtomicBoolean(false);    private final AtomicBoolean initializing = new AtomicBoolean(false);    public CronTaskLoader(SchedulingConfiguration schedulingConfiguration) {        this.schedulingConfiguration = schedulingConfiguration;    }    /**     * 定時任務配置刷新     */    @Scheduled(fixedDelay = 5000)    public void cronTaskConfigRefresh() {        if (appStarted.get() && initializing.compareAndSet(false, true)) {            log.info("定時調度任務動態加載開始>>>>>>");            try {                schedulingConfiguration.refresh();            } finally {                initializing.set(false);            }            log.info("定時調度任務動態加載結束<<<<<<");        }    }    @Override    public void run(ApplicationArguments args) {        if (appStarted.compareAndSet(false, true)) {            cronTaskConfigRefresh();        }    }}

當然,也可以把這部分代碼直接整合到SchedulingConfiguration中,但是為了方便擴展,這里還是將執行與觸發分離了。畢竟除了通過定時任務觸發刷新,還可以在界面上通過按鈕手動觸發刷新,或者通過消息機制回調刷新。這一部分就請大家根據實際業務情況來自由發揮了。PrK28資訊網——每日最新資訊28at.com

驗證

我們創建一個原型工程和三個簡單的定時任務來驗證下,第一個任務是執行周期固定的任務,假設它的Cron表達式永遠不會發生變化,像這樣:PrK28資訊網——每日最新資訊28at.com

@Servicepublic class CronTaskBar implements IPollableService {    @Override    public void poll() {        System.out.println("Say Bar");    }    @Override    public String getCronExpression() {        return "0/1 * * * * ?";    }}

第二個任務是一個經常更換執行周期的任務,我們用一個隨機數發生器來模擬它的善變:PrK28資訊網——每日最新資訊28at.com

@Servicepublic class CronTaskFoo implements IPollableService {    private static final Random random = new SecureRandom();    @Override    public void poll() {        System.out.println("Say Foo");    }    @Override    public String getCronExpression() {        return "0/" + (random.nextInt(9) + 1) + " * * * * ?";    }}

第三個任務就厲害了,它仿佛就像一個電燈的開關,在啟用和禁用中反復橫跳:PrK28資訊網——每日最新資訊28at.com

@Servicepublic class CronTaskUnavailable implements IPollableService {    private String cronExpression = "-";    private static final Map<String, String> map = new HashMap<>();    static {        map.put("-", "0/1 * * * * ?");        map.put("0/1 * * * * ?", "-");    }    @Override    public void poll() {        System.out.println("Say Unavailable");    }    @Override    public String getCronExpression() {        return (cronExpression = map.get(cronExpression));    }}

如果上面的步驟都做對了,日志里應該能看到類似這樣的輸出:PrK28資訊網——每日最新資訊28at.com

定時調度任務動態加載開始>>>>>>定時任務[CronTaskBar]的任務表達式未發生變化,無需刷新定時任務[CronTaskFoo]已加載,當前任務表達式為[0/6 * * * * ?]定時任務[CronTaskUnavailable]的任務表達式配置為禁用,將被不會被調度執行定時調度任務動態加載結束<<<<<<Say BarSay BarSay FooSay BarSay BarSay Bar定時調度任務動態加載開始>>>>>>定時任務[CronTaskBar]的任務表達式未發生變化,無需刷新定時任務[CronTaskFoo]已加載,當前任務表達式為[0/3 * * * * ?]定時任務[CronTaskUnavailable]已加載,當前任務表達式為[0/1 * * * * ?]定時調度任務動態加載結束<<<<<<Say UnavailableSay BarSay UnavailableSay BarSay FooSay UnavailableSay BarSay UnavailableSay BarSay UnavailableSay Bar

小結

我們在上文通過定時刷新和重建任務的方式來實現了動態更改Cron表達式的需求,能夠滿足大部分的項目場景,而且沒有引入quartzs等額外的中間件,可以說是十分的輕量和優雅了。PrK28資訊網——每日最新資訊28at.com

本文鏈接:http://m.www897cc.com/showinfo-26-76560-0.htmlSpring中Cron表達式的優雅實現方案

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 我們一起聊聊如何保證接口冪等性?高并發下的接口冪等性如何實現?

下一篇: Spring事件如何異步執行?

標簽:
  • 熱門焦點
Top 日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不
欧美日韩综合不卡| 久久婷婷国产综合国色天香| 亚洲精品久久久久中文字幕欢迎你 | 久热re这里精品视频在线6| 欧美国产日韩免费| 国产精品久久久久久久浪潮网站 | 欧美三级视频在线播放| 欧美体内谢she精2性欧美| 国产欧亚日韩视频| 亚洲精品黄网在线观看| 亚洲一区在线直播| 美女日韩欧美| 国产精品久久久久久久久免费桃花| 激情视频一区| 亚洲视频免费在线| 久久久久久久久久久久久久一区| 欧美精品网站| 国产在线欧美| 一个人看的www久久| 欧美专区在线观看| 欧美日本免费一区二区三区| 国产亚洲欧美另类一区二区三区| 亚洲精品美女| 欧美在线视频a| 欧美日韩国产一区二区三区| 国产亚洲精品高潮| 一区二区电影免费在线观看| 久久精品一区| 国产精品成人一区二区网站软件| 欲色影视综合吧| 亚洲欧美日韩国产一区二区三区| 欧美sm视频| 国产综合久久久久久鬼色| 99视频精品免费观看| 久久只精品国产| 国产日韩欧美精品综合| 一区二区不卡在线视频 午夜欧美不卡' | 亚洲国产欧美精品| 欧美在现视频| 国产精品福利在线观看网址| 在线观看国产成人av片| 午夜精品久久久久久久白皮肤| 欧美激情一区二区三区高清视频 | 久热精品视频在线观看| 国产精品夜色7777狼人| 亚洲少妇中出一区| 欧美韩日高清| 在线播放亚洲| 久久不射电影网| 国产精品日韩欧美大师| 日韩视频在线一区二区三区| 男同欧美伦乱| 国产一区二区三区久久悠悠色av | 国产揄拍国内精品对白| 亚洲免费在线播放| 欧美性猛交xxxx免费看久久久| 亚洲黄色在线看| 久久影院午夜论| 国产综合网站| 欧美一区二区三区视频免费| 国产精品久久久久久久久搜平片| 亚洲区一区二| 欧美成人一品| 最新热久久免费视频| 裸体女人亚洲精品一区| 精品成人在线视频| 久久国产精品72免费观看| 国产精品综合av一区二区国产馆| 国产精品99久久久久久人| 欧美日韩大片| 一区二区三区久久久| 欧美日韩国产区| 日韩一二三在线视频播| 欧美精品麻豆| 日韩亚洲精品电影| 欧美日韩成人免费| 正在播放欧美一区| 欧美网站在线| 亚洲欧美激情一区| 国产精品综合| 久久久久国产一区二区三区四区 | 亚洲欧美另类在线观看| 欧美婷婷久久| 亚洲欧美三级在线| 国产精品视频在线观看| 亚洲欧美激情诱惑| 国产女主播一区二区三区| 欧美一区二区三区四区高清| 国产三级欧美三级日产三级99| 欧美亚洲视频在线观看| 国内外成人在线视频| 老色批av在线精品| 亚洲高清资源| 欧美日韩美女| 午夜精品国产更新| 国产一区二区丝袜高跟鞋图片| 久久久蜜臀国产一区二区| 在线观看欧美激情| 欧美精品粉嫩高潮一区二区 | 亚洲伦理一区| 国产精品xxxav免费视频| 亚洲在线观看视频| 国产在线观看一区| 欧美xx69| 中文欧美在线视频| 国产欧美一区二区精品性| 久久一二三国产| 夜夜嗨av一区二区三区四区| 国产精品美腿一区在线看| 欧美在线亚洲在线| 亚洲国产另类久久精品| 欧美日韩一区三区四区| 性18欧美另类| 在线看成人片| 国产精品成人一区二区网站软件 | 亚洲午夜未删减在线观看| 国产三级精品在线不卡| 欧美波霸影院| 亚洲一区精彩视频| 韩国av一区二区三区四区| 欧美国产精品| 午夜精品福利视频| 亚洲国产mv| 国产精品成人免费| 久久久噜噜噜久久中文字幕色伊伊 | 欧美视频久久| 久久精品亚洲国产奇米99| 亚洲看片一区| 国产亚洲一区二区三区在线播放| 欧美国产极速在线| 欧美一区2区三区4区公司二百 | 久久久久久久国产| av成人福利| 国内精品视频一区| 欧美日韩一区精品| 久久久久欧美精品| 在线亚洲欧美| 1769国内精品视频在线播放| 国产精品国产三级国产普通话三级 | 亚洲人午夜精品免费| 国产免费亚洲高清| 欧美精品一区二区三区视频| 欧美一区二区三区的| 亚洲免费观看高清在线观看| 国户精品久久久久久久久久久不卡| 欧美日本一区二区视频在线观看| 欧美一区二区视频观看视频| 亚洲人成网站影音先锋播放| 国产亚洲人成a一在线v站| 欧美日韩在线精品| 美日韩丰满少妇在线观看| 先锋影音久久久| av成人免费观看| 亚洲第一视频| 国产日韩三区| 国产精品www色诱视频| 欧美 日韩 国产精品免费观看| 欧美亚洲一区| 一本久久综合亚洲鲁鲁| 亚洲国产精品一区二区www| 国产午夜亚洲精品不卡| 欧美午夜片在线观看| 欧美成人中文字幕| 久久久久久电影| 亚洲欧美视频在线观看视频| 一本综合久久| 亚洲精品小视频| 亚洲大胆视频| 精品av久久久久电影| 国产欧美三级| 国产精品激情电影| 欧美日韩裸体免费视频| 欧美激情久久久| 你懂的国产精品| 久久国产精品电影| 亚洲欧美日韩爽爽影院| 亚洲视频碰碰| 亚洲美女视频在线观看| 亚洲国产日韩一区| 在线观看av不卡| 黄色成人在线网址| 国产亚洲午夜高清国产拍精品| 国产精品久久久久久模特 | 久久综合图片| 久久精品天堂| 欧美一区二区视频97| 欧美亚洲系列| 欧美影院一区| 欧美在线免费观看视频| 久久福利电影| 久久精品人人爽| 久久婷婷影院| 久久综合九色综合欧美就去吻| 久久久综合网| 久久综合色婷婷| 快射av在线播放一区| 六月婷婷久久| 欧美成熟视频| 欧美精品日韩精品| 欧美日韩久久| 欧美私人啪啪vps| 欧美性猛交视频| 国产精品亚洲一区| 国产偷国产偷亚洲高清97cao |