Apache DolphinScheduler 的定時任務配置使用 7 位元 Crontab 表達式,分別對應秒、分、小時、日、月、週、年。
我們團隊的日常開發工作中,工作流程的時序調度一般不需要詳細到秒級。但歷史上也曾出現過因配置錯誤而導致故障的情況,例如原本應該每分鐘執行一次的工作流程被錯誤配置為每秒執行一次,導致短時間內產生大量工作流程實例,影響工作效率。 Apache 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> <h3> Server-side </h3> <p>Add Crontab expression validation (there are two places: one is the new POST interface, and the other is the modified PUT interface), directly add a validation method for these two places to call:<br> </p> <pre class="brush:php;toolbar:false"> if (scheduleParam.getCrontab().startsWith("*")) { logger.error("The crontab must not start with *"); putMsg(result, Status.CRONTAB_EVERY_SECOND_ERROR); return result; }
本文到此結束。
以上是Apache DolphinScheduler 限制二級時序調度的詳細內容。更多資訊請關注PHP中文網其他相關文章!