设计一个定时任务管理工具,使用 shell 脚本实现,核心目标是实现每秒执行一次,并保证任务异步、非阻塞执行。以下是实现的详细设计:
工具功能
任务注册:支持注册新任务。
任务删除:支持删除已有任务。
任务执行:每秒执行一次任务。
异步非阻塞:任务在独立的进程中执行,不影响主进程。
日志记录:任务执行结果记录到日志文件中。
脚本设计
#!/bin/bash
# 配置文件和日志文件路径
config_file="./tasks.conf"
log_file="./tasks.log"
pid_file="./scheduler.pid"
reload_signal_file="./reload.signal"
# 初始化配置文件和信号文件
if [ ! -f "$config_file" ]; then
touch "$config_file"
fi
if [ -f "$reload_signal_file" ]; then
rm -f "$reload_signal_file"
fi
# 任务状态跟踪表
declare -a last_execution_times
# 定时调度器
scheduler() {
echo $$ > "$pid_file"
echo "调度器启动,pid: $$"
while true; do
# 检查是否需要重新加载配置
if [ -f "$reload_signal_file" ]; then
echo "$(date '+%y-%m-%d %h:%m:%s') [info] 重新加载配置" >> "$log_file"
last_execution_times=() # 清空上次执行记录
rm -f "$reload_signal_file"
fi
# 当前时间戳
local current_time=$(date +%s)
# 动态加载配置文件并执行任务
while ifs="=" read -r task_name interval command; do
[ -z "$task_name" ] || [ -z "$interval" ] || [ -z "$command" ] && continue
# 获取上次执行时间,如果没有记录,默认为0
local last_exec_time=${last_execution_times[$task_name]:-0}
# 判断是否到达执行间隔
if (( current_time - last_exec_time >= interval )); then
( #安装任务名记录任务执行情况
local task_log_file="$task_name/$(date '+%y-%m-%d').log"
if [ ! -d "$(dirname "$task_log_file")" ]; then
mkdir -p "$(dirname "$task_log_file")"
fi
echo "$(date +'%h:%m:%s')执行start" >> "$task_log_file"
eval "$command" >> "$task_log_file" 2>&1
echo "end" >> "$task_log_file"
) &
last_execution_times[$task_name]=$current_time # 更新任务的最后执行时间
fi
done < "$config_file"
# 等待 1 秒
sleep 1
done
}
# 停止调度器
stop_scheduler() {
if [ -f "$pid_file" ]; then
local pid
pid=$(cat "$pid_file")
kill "$pid" && rm -f "$pid_file"
echo "调度器已停止"
else
echo "调度器未运行"
fi
}
# 添加任务
add_task() {
local task_name="$1"
local interval="$2"
local command="$3"
if grep -q "^$task_name=" "$config_file"; then
echo "任务 '$task_name' 已存在"
return 1
fi
echo "$task_name=$interval=$command" >> "$config_file"
echo "任务 '$task_name' 添加成功"
}
# 删除任务
remove_task() {
local task_name="$1"
if ! grep -q "^$task_name=" "$config_file"; then
echo "任务 '$task_name' 不存在"
return 1
fi
sed -i "/^$task_name=/d" "$config_file"
echo "任务 '$task_name' 已删除"
}
# 重新加载配置
reload_scheduler() {
if [ ! -f "$pid_file" ]; then
echo "调度器未运行,无法重新加载配置"
return 1
fi
touch "$reload_signal_file"
echo "配置重新加载信号已发送"
}
# 显示帮助
show_help() {
echo "用法: $0 [命令] [参数]"
echo "命令:"
echo " start 启动调度器"
echo " stop 停止调度器"
echo " reload 重新加载配置文件"
echo " add 任务名 间隔秒数 命令 添加任务到配置文件"
echo " remove 任务名 从配置文件中删除任务"
echo " help 显示帮助"
}
# 主逻辑
case "$1" in
start)
scheduler
;;
stop)
stop_scheduler
;;
reload)
reload_scheduler
;;
add)
add_task "$2" "$3" "$4"
;;
remove)
remove_task "$2"
;;
help|*)
show_help
;;
esac
配置文件格式
使用 tasks.conf 文件存储任务配置,每行一个任务,格式为:
任务名=间隔秒数=命令
例如:
task1=5=echo '每5秒运行一次' task2=30=date task3=10=sleep 2 && echo '任务完成'
启动调度器
./scheduler.sh start
添加任务
./scheduler.sh add "task1" 5 "echo 'hello, every 5 seconds!'"
删除任务
./scheduler.sh remove "task1"
重新加载配置
./scheduler.sh reload
停止调度器
./scheduler.sh stop
同时支持手动修改配置文件修改任务
实现原理
任务间隔控制:使用关联数组 last_execution_times 记录每个任务的最后执行时间。
动态间隔判断:在每秒循环中,检查当前时间与任务的最后执行时间的差值是否达到设定的间隔秒数。
动态配置管理:配置文件支持实时修改,结合 reload 功能可立即生效。
异步执行:每个任务独立进程执行,不阻塞其他任务。
优点
支持自定义间隔:满足不同任务执行频率的需求。
动态加载:无需重启即可更新任务配置。
高可扩展性:可根据需要进一步优化,如添加任务优先级或日志清理功能。
为定时器创建 systemd 服务
创建一个新的 systemd 服务文件:你可以将服务单元文件放在 /etc/systemd/system/ 目录下。假设我们创建一个名为 task-scheduler.service 的服务。
sudo nano /etc/systemd/system/task-scheduler.service
编辑服务单元文件:以下是 task-scheduler.service 的内容模板。你可以根据需要修改。
[unit] description=task scheduler service after=network.target [service] type=simple user=root execstart=/path/to/your/scheduler.sh start execreload=/path/to/your/scheduler.sh reload execstop=/path/to/your/scheduler.sh stop workingdirectory=/path/to/working/directory restart=always restartsec=5 standardoutput=journal standarderror=journal syslogidentifier=task-scheduler [install] wantedby=multi-user.target
重新加载 systemd 配置:让 systemd 识别新的服务单元文件。
sudo systemctl daemon-reload
启用服务:设置服务在系统启动时自动启动。
sudo systemctl enable task-scheduler.service
开机启动服务:立即启动任务调度器服务。
sudo systemctl start task-scheduler.service
检查服务状态:确认服务是否正常启动。
sudo systemctl status task-scheduler.service
停止服务:
sudo systemctl stop task-scheduler.service
重新加载配置(如果你修改了配置文件或脚本):
sudo systemctl reload task-scheduler.service
可以通过 journalctl 命令查看服务日志,以便调试:
sudo journalctl -u task-scheduler.service
到此这篇关于用shell脚本自己一个秒级定时任务的文章就介绍到这了,更多相关shell定时任务内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论