首页 > 其他分享 >DolphinScheduler 限制秒级别的定时调度

DolphinScheduler 限制秒级别的定时调度

时间:2024-11-06 18:43:30浏览次数:1  
标签:PropType const intervalPerformRef DolphinScheduler intervalStartRef 调度 value tim

背景

DolphinScheduler 定时任务配置采用的 7 位 Crontab 表达式,分别对应 秒、分、时、月天、月、周天、年
在团队日常开发工作中,工作流的定时调度一般不会细化到秒级别。但历史上出现过因配置的疏忽大意而产生故障时间,如应该配置每分钟执行的工作流被配置长了每秒执行,造成短时间内产上大量工作流实例,对 DolphinScheduler 服务可用性和提交任务的 Hadoop 集群造成影响。
基于此,团队决定将 DolphinScheduler 中定时任务配置模块的 Crontab 表达式做限制,从平台侧杜绝此类事件发生。

方案

我们的方案是从前后端双方面限制 Crontab 表达式的第一位:

  • 前端配置选择不提供“每一秒钟”选项
  • 服务端接口判断第一位为 * 时,返回错误

前端修改

在前端项目中,秒、分、时 均为统一模版(CrontabTime),因此新增 dolphinscheduler-ui/src/components/crontab/modules/second.tsx ,只保留两种模式:intervalTime 和 specificTime

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
import _ from 'lodash'
import { defineComponent, onMounted, PropType, ref, toRefs, watch } from 'vue'
import { NInputNumber, NRadio, NRadioGroup, NSelect } from 'naive-ui'
import { useI18n } from 'vue-i18n'
import { ICrontabI18n } from '../types'
import { isStr, specificList } from '../common'
import styles from '../index.module.scss'
 
const props = {
    timeMin: {
        type: Number as PropType<number>,
        default: 0
    },
    timeMax: {
        type: Number as PropType<number>,
        default: 60
    },
    intervalPerform: {
        type: Number as PropType<number>,
        default: 5
    },
    intervalStart: {
        type: Number as PropType<number>,
        default: 3
    },
    timeSpecial: {
        type: Number as PropType<number | string>,
        default: 60
    },
    timeValue: {
        type: String as PropType<string>,
        default: '*'
    },
    timeI18n: {
        type: Object as PropType<ICrontabI18n>,
        require: true
    }
}
 
export default defineComponent({
    name: 'CrontabSecond',
    props,
    emits: ['update:timeValue'],
    setup(props, ctx) {
        const options = Array.from({ length: 60 }, (x, i) => ({
            label: i.toString(),
            value: i
        }))
 
        const timeRef = ref()
        const radioRef = ref()
        const intervalStartRef = ref(props.intervalStart)
        const intervalPerformRef = ref(props.intervalPerform)
        const specificTimesRef = ref<Array<number>>([])
 
        /**
         * Parse parameter value
         */
        const analyticalValue = () => {
            const $timeVal = props.timeValue
            // Interval time
            const $interval = isStr($timeVal, '/')
            // Specific time
            const $specific = isStr($timeVal, ',')
 
            // Positive integer (times)
            if (
                ($timeVal.length === 1 ||
                    $timeVal.length === 2 ||
                    $timeVal.length === 4) &&
                _.isInteger(parseInt($timeVal))
            ) {
                radioRef.value = 'specificTime'
                specificTimesRef.value = [parseInt($timeVal)]
                return
            }
 
            // Interval times
            if ($interval) {
                radioRef.value = 'intervalTime'
                intervalStartRef.value = parseInt($interval[0])
                intervalPerformRef.value = parseInt($interval[1])
                timeRef.value = `${intervalStartRef.value}/${intervalPerformRef.value}`
                return
            }
 
            // Specific times
            if ($specific) {
                radioRef.value = 'specificTime'
                specificTimesRef.value = $specific.map((item) => parseInt(item))
                return
            }
        }
 
        // Interval start time(1)
        const onIntervalStart = (value: number | null) => {
            intervalStartRef.value = value || 0
            if (radioRef.value === 'intervalTime') {
                timeRef.value = `${intervalStartRef.value}/${intervalPerformRef.value}`
            }
        }
 
        // Interval execution time(2)
        const onIntervalPerform = (value: number | null) => {
            intervalPerformRef.value = value || 0
            if (radioRef.value === 'intervalTime') {
                timeRef.value = `${intervalStartRef.value}/${intervalPerformRef.value}`
            }
        }
 
        // Specific time
        const onSpecificTimes = (arr: Array<number>) => {
            specificTimesRef.value = arr
            if (radioRef.value === 'specificTime') {
                specificReset()
            }
        }
 
        // Reset interval time
        const intervalReset = () => {
            timeRef.value = `${intervalStartRef.value}/${intervalPerformRef.value}`
        }
 
        // Reset specific time
        const specificReset = () => {
            let timeValue = '0'
            if (specificTimesRef.value.length) {
                timeValue = specificTimesRef.value.join(',')
            }
            timeRef.value = timeValue
        }
 
        const updateRadioTime = (value: string) => {
            switch (value) {
                case 'intervalTime':
                    intervalReset()
                    break
                case 'specificTime':
                    specificReset()
                    break
            }
        }
 
        watch(
            () => timeRef.value,
            () => ctx.emit('update:timeValue', timeRef.value.toString())
        )
 
        onMounted(() => analyticalValue())
 
        return {
            options,
            radioRef,
            intervalStartRef,
            intervalPerformRef,
            specificTimesRef,
            updateRadioTime,
            onIntervalStart,
            onIntervalPerform,
            onSpecificTimes,
            ...toRefs(props)
        }
    },
    render() {
        const { t } = useI18n()
 
        return (
            <NRadioGroup
                v-model:value={this.radioRef}
                onUpdateValue={this.updateRadioTime}
            >
                <div class={styles['crontab-list']}>
                    <NRadio value={'intervalTime'} />
                    <div class={styles['crontab-list-item']}>
                        <div class={styles['item-text']}>{t(this.timeI18n!.every)}</div>
                        <div class={styles['number-input']}>
                            <NInputNumber
                                defaultValue={5}
                                min={this.timeMin}
                                max={this.timeMax}
                                v-model:value={this.intervalPerformRef}
                                onUpdateValue={this.onIntervalPerform}
                            />
                        </div>
                        <div class={styles['item-text']}>
                                {t(this.timeI18n!.timeCarriedOut)}
                        </div>
                        <div class={styles['number-input']}>
                            <NInputNumber
                                defaultValue={3}
                                min={this.timeMin}
                                max={this.timeMax}
                                v-model:value={this.intervalStartRef}
                                onUpdateValue={this.onIntervalStart}
                            />
                        </div>
                        <div class={styles['item-text']}>{t(this.timeI18n!.timeStart)}</div>
                    </div>
                </div>
                <div class={styles['crontab-list']}>
                    <NRadio value={'specificTime'} />
                    <div class={styles['crontab-list-item']}>
                        <div>{t(this.timeI18n!.specificTime)}</div>
                        <div class={styles['select-input']}>
                            <NSelect
                                multiple
                                options={specificList[this.timeSpecial]}
                                placeholder={t(this.timeI18n!.specificTimeTip)}
                                v-model:value={this.specificTimesRef}
                                onUpdateValue={this.onSpecificTimes}
                            />
                        </div>
                    </div>
                </div>
            </NRadioGroup>
        )
    }
})

并在 dolphinscheduler-ui/src/components/crontab/index.tsx 中,将 Second 由 CrontabTime 改成 CrontabSecond

       <NTabPane name='second' tab={t('crontab.second')}>
          <CrontabSecond
            v-model:timeValue={this.secondRef}
            timeI18n={timeI18n.second}
            timeSpecial={60}
            timeMin={0}
            timeMax={59}
          />
        </NTabPane>

服务端

添加crontab表达式检验(有两处:一处是新增Post接口、另一处是修改PUT接口),直接添加个检测方法供这两处调用:

        if (scheduleParam.getCrontab().startsWith("*")) {
            logger.error("The crontab must not start with *");
            putMsg(result, Status.CRONTAB_EVERY_SECOND_ERROR);
            return result;
        }

标签:PropType,const,intervalPerformRef,DolphinScheduler,intervalStartRef,调度,value,tim
From: https://www.cnblogs.com/ikinson/p/18530764

相关文章

  • Js Bom&定时器、事件循环
    BOM:浏览器对象模型BOM为我们提供了一组对象,通过这组对象可以完成对浏览器的各种操作BOM对象:Window——代表浏览器窗口(全局对象)Navigator——浏览器的对象(可以用来识别浏览器)Location——浏览器的地址栏信息History——浏览器的历史记录(控制浏览器前进后退)......
  • jmeter 固定吞吐量定时器
    使用:模拟18000QPS查询 HitsPerSecondTPS:聚合报告:   Targetthroughput(insamplesperminute):目标吞吐量(单位分钟),即每分钟执行多少次(TPM)CalculateThroughputbasedon(计算吞吐量策略):1、Thisthreadonly:仅对当前线程,也就是每个线程相互是不干扰的,都......
  • 京东物流-智能运输调度系统方案 荣获IF、红点国际设计大奖
    作者:京东物流王文玲   前言京东集团企业文化升级后,「以技术为本,让生活更美好」成为京东人的使命,在「创新」价值观引导下,设计师基于对物流业务领域持续深耕,自驱发起智能调度解决方案的创新思考,推演得到智能物流运输调度系统概念方案,经过投稿先后获得设计领域国际影响力较......
  • 【HAProxy04】企业级反向代理HAProxy调度算法之Socat 工具
    HAProxy调度算法HAProxy通过固定参数balance指明对后端服务器的调度算法,该参数可以配置在listen或backend选项中。HAProxy的调度算法分为静态和动态调度算法,但是有些算法可以根据不同的参数实现静态和动态算法相互转换。官方文档:HAProxyversion2.4.15-Configurati......
  • 基于条件风险价值CVaR的微网动态定价与调度策略(Matlab代码实现)
    ......
  • 基于条件风险价值CVaR的微网动态定价与调度策略(Matlab代码实现)
    ......
  • cron定时任务报错PAM unable to dlopen(pam_tally2.so): /lib/security/pam_tally2.so
    在Ubuntu22.04中,pam_tally2模块可能已经不再被支持或包含在系统默认的PAM模块中。因此,当系统尝试加载该模块时,无法找到对应的.so文件,需要使用pam_faillock.so来代替先查找下是否存在pam_faillock.so模块find/usr-name"pam_*.so"没有pam_tally2.so,复制出来一个就解决了......
  • .net core 使用定时任务 quartz.net 实例
    项目是core3.1的 只用引用一个包就可以了引用配置Quartz首先创建一个任务工厂publicclassCronJobFactory:IJobFactory{privatereadonlyIServiceProvider_serviceProvider;publicCronJobFactory(IServiceProviderserviceProvider){_ser......
  • 稀疏感知&稀疏预定义数据调度器
    稀疏感知的数据调度器和稀疏预定义的数据调度器虽然都针对稀疏数据的高效调度,但在处理方式和灵活性上有所不同。稀疏感知数据调度器(Sparse-AwareScheduler)稀疏感知数据调度器的核心在于其动态适应性和智能调度能力。它不仅知道数据是稀疏的,还能动态识别稀疏性模式并实时适应调......
  • 实景三维赋能森林防灭火指挥调度智慧化
    森林防灭火工作是保护森林资源和生态环境的重要任务。随着信息技术的发展,实景三维技术在森林防灭火指挥调度中的应用日益广泛,为提升防灭火工作的效率和效果提供了有力支持。一、森林防灭火面临的挑战森林火灾具有突发性强、破坏性大、蔓延速度快、扑救困难等特点......