提交 65e7d726 authored 作者: 陈泽健's avatar 陈泽健

feat(scripts): 优化定时任务脚本和监控脚本

- 支持多个定时任务配置,新增每天凌晨1点执行的服务健康检查任务
- 实现参数化控制功能,通过--enable/--disable参数控制任务启停
- 添加重试机制到服务监测脚本,避免网络波动导致错误判断
- 引入文件锁机制防止定时任务并发执行冲突
- 实现自动清理功能,脚本不存在时自动移除对应的crontab条目
- 添加详细日志记录和错误处理机制
- 支持参数冲突检测和默认向后兼容行为
上级 2a283bbe
...@@ -10,12 +10,31 @@ ...@@ -10,12 +10,31 @@
### 需求描述 ### 需求描述
#### 调用逻辑 #### 调用逻辑
- 当前是只配置了一个定时任务,需要修改为可配置多个定时任务。 - 当前是只配置了一个定时任务,需要修改为可配置多个定时任务。
- 不需要通过主脚本传参控制定时任务配置,主脚本只需要调用这个`auto_crontab_settings.sh`脚本,脚本内部实现多个定时任务配置。 - 支持通过参数控制每个定时任务的启用/禁用,便于部署阶段的不同选择。
- 主脚本调用方式:`./auto_crontab_settings.sh [参数]`
- 新增定时任务: - 新增定时任务:
- 定时周期:每天凌晨01:00执行 - 定时周期:每天凌晨01:00执行
- 定时任务执行脚本:`/data/services/scripts/check_server_health.sh` - 定时任务执行脚本:`/data/services/scripts/check_server_health.sh`
- 需要先判断该路径是否存在脚本,若不存在则不进行配置,打印相关日志记录。 - 需要先判断该路径是否存在脚本,若不存在则不进行配置,打印相关日志记录。
#### 参数控制设计
- **参数方式:** 使用 `--enable-xxx` / `--disable-xxx` 开关式参数
- **默认行为:** 不传参数时默认全部启用(向后兼容)
- **参数示例:**
```bash
# 默认全部启用
./auto_crontab_settings.sh
# 只启用 check_server_health
./auto_crontab_settings.sh --disable-ujava2
# 禁用 check_server_health
./auto_crontab_settings.sh --disable-check-health
# 显式启用指定任务
./auto_crontab_settings.sh --enable-ujava2 --enable-check-health
```
## 疑问与解答记录 ## 疑问与解答记录
### 疑问1:现有定时任务是否保留? ### 疑问1:现有定时任务是否保留?
...@@ -38,6 +57,42 @@ ...@@ -38,6 +57,42 @@
- **问题:** 计划将现有硬编码的 `add_crontab_job()` 重构为通用任务添加函数(接受脚本路径、cron 表达式、日志路径作为参数),然后在脚本内部分别调用两次来配置两个任务。 - **问题:** 计划将现有硬编码的 `add_crontab_job()` 重构为通用任务添加函数(接受脚本路径、cron 表达式、日志路径作为参数),然后在脚本内部分别调用两次来配置两个任务。
- **解答:** 改为通用函数,后期还会有其他定时任务脚本增加,需要预留扩展能力。 - **解答:** 改为通用函数,后期还会有其他定时任务脚本增加,需要预留扩展能力。
### 疑问6:执行用户确认?
- **问题:** 现有任务 `*/3 * * * * ujava2-startup.sh` 没有显式指定执行用户。新增任务的执行用户应与现有任务保持一致?
- **解答:** 执行用户与现有任务保持一致。
### 疑问7:日志输出格式?
- **问题:** 需求提到"打印相关日志记录",但没有明确日志输出到哪里、格式是什么。
- **解答:** 无需打印日志,ujava2-startup.sh 脚本自身会打印。
### 疑问8:Crontab 注释标记?
- **问题:** 是否需要在 crontab 中为每个定时任务添加注释标记以便识别和管理?
- **解答:** 增加对应注释标记。
### 疑问9:重复运行保护?
- **问题:** 服务健康检查脚本如果上次执行未完成,是否需要防止重复运行?
- **解答:** 无需进行重复运行保护。
### 疑问10:脚本删除后的清理机制?
- **问题:** 如果 `check_server_health.sh` 最初存在并被配置,但后续被删除了,crontab 中的任务条目是否需要自动清理?
- **解答:** 选择自动清理方式,保持 crontab 与实际文件的一致性。脚本每次运行时检查所有配置的任务,若脚本不存在则移除对应 crontab 条目。
### 疑问11:是否需要参数化控制?
- **问题:** 主脚本调用 `auto_crontab_settings.sh` 时,是否需要通过参数控制每个定时任务的启用/禁用?
- **解答:** 需要参数化控制,以便在部署阶段进行不同选择。
### 疑问12:参数方式选择?
- **问题:** 参数化控制有环境变量、配置文件、命令行参数等多种方式,选择哪种?
- **解答:** 选择方式A,使用 `--enable-xxx / --disable-xxx` 开关式命令行参数。
### 疑问13:默认行为是什么?
- **问题:** 如果调用时不传任何参数,默认应该启用哪些任务?
- **解答:** 默认全部启用(向后兼容现有行为)。
### 疑问14:参数冲突如何处理?
- **问题:** 如果同时传入矛盾的参数(如 `--enable-xxx``--disable-xxx`),如何处理?
- **解答:** 如果重复传参(同时启用和禁用同一任务),则跳过该任务配置并打印警告日志。
## 规范文档 ## 规范文档
- 代码规范: `Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md` - 代码规范: `Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 问题总结: `Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md` - 问题总结: `Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
......
# 定时任务脚本优化计划执行文档
> 基于需求文档:`_PRD_定时任务脚本优化_需求文档.md`
## 1. 代码路径
- **需调用代码路径**`自动化部署脚本/x86架构/新统一平台/auto_crontab_settings.sh`
- **主脚本**`自动化部署脚本/x86架构/新统一平台/new_auto.sh`
---
## 2. 重构目标
### 2.1 核心目标
将单一硬编码定时任务配置脚本重构为支持多任务配置的通用框架。
### 2.2 具体要求
- [ ] 支持配置多个定时任务
- [ ] 支持参数化控制:通过 `--enable-xxx/--disable-xxx` 控制任务启用/禁用
- [ ] 默认行为:不传参数时全部启用(向后兼容)
- [ ] 参数冲突处理:同一任务同时启用和禁用时跳过并打印警告
- [ ] 预留扩展能力,便于后续添加新的定时任务
- [ ] 自动清理机制:脚本不存在时移除对应的 crontab 条目
- [ ] 每个任务添加注释标记便于识别管理
---
## 3. 现有定时任务
| 任务 | Cron 表达式 | 脚本路径 | 日志路径 |
|------|-------------|----------|----------|
| ujava2-startup | `*/3 * * * *` | `/data/services/scripts/ujava2-startup.sh` | `/var/log/ujava2-cron.log` |
---
## 4. 新增定时任务
| 任务 | Cron 表达式 | 脚本路径 | 日志 | 备注 |
|------|-------------|----------|------|------|
| check_server_health | `0 1 * * *` | `/data/services/scripts/check_server_health.sh` | 脚本自身管理 | 每天凌晨 01:00 执行 |
---
## 5. 重构设计
### 5.1 通用函数设计
将现有 `add_crontab_job()` 重构为通用函数,接受以下参数:
```bash
add_crontab_job \
"<cron_expression>" \
"<script_path>" \
"<log_path>" \
"<comment>"
```
**参数说明:**
| 参数 | 说明 | 示例 |
|------|------|------|
| `cron_expression` | Cron 时间表达式 | `"0 1 * * *"` |
| `script_path` | 脚本绝对路径 | `/data/services/scripts/check_server_health.sh` |
| `log_path` | 日志文件路径(可选) | `/var/log/xxx.log` 或空字符串 |
| `comment` | 注释标记 | `"自动配置 - check_server_health"` |
### 5.2 函数处理逻辑
#### 5.2.1 脚本存在性检查
- [ ] 检查脚本文件是否存在
- [ ] 若不存在:记录日志并跳过配置
- [ ] 若存在:继续检查执行权限
#### 5.2.2 执行权限检查
- [ ] 检查脚本是否具有可执行权限
- [ ] 若无权限:自动执行 `chmod +x`
- [ ] 若修复失败:记录错误并跳过
#### 5.2.3 Crontab 配置
- [ ] 生成带注释的任务条目
- [ ] 检查是否已存在(避免重复)
- [ ] 追加新任务到 crontab
#### 5.2.4 自动清理机制
- [ ] 遍历当前 crontab 中所有由本脚本管理的任务
- [ ] 检查任务对应的脚本是否存在
- [ ] 若不存在:移除该 crontab 条目并记录日志
### 5.3 任务标记方式
使用特定注释标记识别由本脚本管理的任务:
```bash
# [AUTO_CRON] ujava2-startup
*/3 * * * * /data/services/scripts/ujava2-startup.sh >> /var/log/ujava2-cron.log 2>&1
# [AUTO_CRON] check_server_health
0 1 * * * /data/services/scripts/check_server_health.sh
```
### 5.4 参数化控制设计
#### 5.4.1 支持的参数
| 参数 | 说明 |
|------|------|
| `--enable-ujava2` | 启用 ujava2-startup 定时任务 |
| `--disable-ujava2` | 禁用 ujava2-startup 定时任务 |
| `--enable-check-health` | 启用 check_server_health 定时任务 |
| `--disable-check-health` | 禁用 check_server_health 定时任务 |
| `-h, --help` | 显示帮助信息 |
#### 5.4.2 参数解析逻辑
```bash
# 任务状态数组(默认全部启用)
declare -A TASK_STATUS
TASK_STATUS[ujava2]=true
TASK_STATUS[check_health]=true
# 解析参数
while [[ $# -gt 0 ]]; do
case "$1" in
--enable-ujava2)
TASK_STATUS[ujava2]=true
;;
--disable-ujava2)
if [[ "${TASK_STATUS[ujava2]}" == "false" ]]; then
log "WARN" "⚠️ 参数冲突:ujava2 同时被启用和禁用,跳过该任务"
TASK_STATUS[ujava2]=conflict
else
TASK_STATUS[ujava2]=false
fi
;;
--enable-check-health)
TASK_STATUS[check_health]=true
;;
--disable-check-health)
if [[ "${TASK_STATUS[check_health]}" == "false" ]]; then
log "WARN" "⚠️ 参数冲突:check_health 同时被启用和禁用,跳过该任务"
TASK_STATUS[check_health]=conflict
else
TASK_STATUS[check_health]=false
fi
;;
-h|--help)
show_help
exit 0
;;
*)
log "ERROR" "⛔ 未知参数: $1"
show_help
exit 1
;;
esac
shift
done
```
#### 5.4.3 任务定义结构
```bash
# 任务定义数组
declare -A TASK_CRON
declare -A TASK_SCRIPT
declare -A TASK_LOG
# ujava2-startup 任务定义
TASK_CRON[ujava2]="*/3 * * * *"
TASK_SCRIPT[ujava2]="/data/services/scripts/ujava2-startup.sh"
TASK_LOG[ujava2]="/var/log/ujava2-cron.log"
# check_server_health 任务定义
TASK_CRON[check_health]="0 1 * * *"
TASK_SCRIPT[check_health]="/data/services/scripts/check_server_health.sh"
TASK_LOG[check_health]=""
```
#### 5.4.4 调用示例
```bash
# 默认全部启用
./auto_crontab_settings.sh
# 只启用 check_server_health
./auto_crontab_settings.sh --disable-ujava2
# 禁用 check_server_health
./auto_crontab_settings.sh --disable-check-health
# 显式启用指定任务
./auto_crontab_settings.sh --enable-ujava2 --enable-check-health
# 参数冲突示例(会跳过 ujava2 任务)
./auto_crontab_settings.sh --enable-ujava2 --disable-ujava2
```
---
## 6. 实现步骤
### 6.1 阶段一:重构通用函数
- [ ] 步骤 1:提取通用函数 `add_crontab_job()`
- 参数化 cron 表达式、脚本路径、日志路径、注释
- 保留原有的检查逻辑(服务状态、脚本存在性、执行权限)
- [ ] 步骤 2:实现注释标记功能
- 在添加任务时插入注释行
- 格式:`# [AUTO_CRON] <task_name>`
- [ ] 步骤 3:优化日志输出
- 统一日志格式(前缀、时间戳)
- 区分 INFO/WARN/ERROR 级别
### 6.2 阶段二:实现自动清理机制
- [ ] 步骤 4:新增 `cleanup_crontab_jobs()` 函数
- 解析当前 crontab
- 识别带 `[AUTO_CRON]` 标记的任务
- 检查对应脚本是否存在
- 移除无效任务条目
- [ ] 步骤 5:集成清理逻辑到主流程
- 在添加新任务前执行清理
- 记录清理结果日志
### 6.3 阶段三:配置新任务
- [ ] 步骤 6:配置 ujava2-startup 任务
- 使用通用函数配置现有任务
- 添加注释标记
- [ ] 步骤 7:配置 check_server_health 任务
- 使用通用函数配置新任务
- 设置 cron 表达式为 `0 1 * * *`
- 无需日志重定向
### 6.4 阶段四:测试验证
- [ ] 步骤 8:功能测试
- 测试单个任务添加
- 测试多个任务配置
- 测试重复任务跳过
- 测试脚本不存在时的处理
- 测试执行权限自动修复
- 测试自动清理机制
- [ ] 步骤 9:日志验证
- 检查日志输出是否正确
- 验证错误信息是否清晰
---
## 7. 关键代码片段设计
### 7.1 通用函数框架
```bash
# ========================================
# ✅ 函数:安全添加定时任务(通用版本)
# ========================================
function add_crontab_job() {
local cron_expression="$1"
local script_path="$2"
local log_path="$3"
local comment="$4"
local task_name="$5"
# ... 检查逻辑 ...
# 生成任务条目
if [[ -n "$log_path" ]]; then
local job="$cron_expression $script_path >> $log_path 2>&1"
else
local job="$cron_expression $script_path"
fi
# ... 配置逻辑 ...
}
```
### 7.2 自动清理函数框架
```bash
# ========================================
# ✅ 函数:清理无效的定时任务
# ========================================
function cleanup_crontab_jobs() {
local crontab_file="/tmp/crontab.cleanup.$RANDOM"
local cleaned=false
crontab -l > "$crontab_file" 2>/dev/null || return 0
# 遍历并检查每个标记的任务
# ... 检查逻辑 ...
if [[ "$cleaned" == true ]]; then
crontab "$crontab_file"
log "INFO" "🧹 已清理无效的定时任务"
fi
rm -f "$crontab_file"
}
```
### 7.3 任务配置调用示例
```bash
# 主函数中调用(基于任务状态配置)
function main() {
# 1. 解析命令行参数
parse_arguments "$@"
# 2. 执行清理
cleanup_crontab_jobs
# 3. 根据任务状态配置任务
for task_name in "${!TASK_STATUS[@]}"; do
local status="${TASK_STATUS[$task_name]}"
# 跳过冲突任务和禁用任务
if [[ "$status" == "conflict" ]]; then
continue
fi
if [[ "$status" == "false" ]]; then
log "INFO" "⏭️ 任务 $task_name 已被禁用,跳过配置"
continue
fi
# 配置启用的任务
add_crontab_job \
"${TASK_CRON[$task_name]}" \
"${TASK_SCRIPT[$task_name]}" \
"${TASK_LOG[$task_name]}" \
"$task_name"
done
}
# 主函数入口
main "$@"
```
### 7.4 帮助信息函数
```bash
# ========================================
# ✅ 函数:显示帮助信息
# ========================================
function show_help() {
cat << EOF
定时任务配置脚本
用法: $0 [选项]
选项:
--enable-ujava2 启用 ujava2-startup 定时任务
--disable-ujava2 禁用 ujava2-startup 定时任务
--enable-check-health 启用 check_server_health 定时任务
--disable-check-health 禁用 check_server_health 定时任务
-h, --help 显示此帮助信息
默认行为:
不传参数时,所有定时任务默认启用
示例:
$0 # 默认启用所有任务
$0 --disable-ujava2 # 只启用 check_server_health
$0 --enable-ujava2 --enable-check-health # 显式启用指定任务
EOF
}
```
---
## 8. 测试用例
| 用例编号 | 测试场景 | 测试命令 | 预期结果 |
|----------|----------|----------|----------|
| TC-01 | 默认全部启用 | `./auto_crontab_settings.sh` | 所有任务成功添加到 crontab |
| TC-02 | 正常添加单个任务 | `./auto_crontab_settings.sh --disable-check-health` | 只添加 ujava2-startup 任务 |
| TC-03 | 禁用指定任务 | `./auto_crontab_settings.sh --disable-ujava2` | 只添加 check_server_health 任务 |
| TC-04 | 显式启用任务 | `./auto_crontab_settings.sh --enable-ujava2 --enable-check-health` | 所有任务成功添加 |
| TC-05 | 参数冲突处理 | `./auto_crontab_settings.sh --enable-ujava2 --disable-ujava2` | 打印冲突警告,跳过 ujava2 任务 |
| TC-06 | 脚本不存在 | (脚本不存在时运行) | 记录错误日志,跳过配置 |
| TC-07 | 脚本无执行权限 | (脚本无权限时运行) | 自动修复权限后成功配置 |
| TC-08 | 重复添加同一任务 | (任务已存在时运行) | 检测到已存在,跳过添加 |
| TC-09 | 脚本被删除后重新运行 | (脚本被删除后运行) | 自动移除无效任务条目 |
| TC-10 | cron 服务未运行 | (cron 未运行时执行) | 自动启动 cron 服务后配置任务 |
| TC-11 | 未知参数 | `./auto_crontab_settings.sh --unknown-param` | 显示错误信息并退出 |
| TC-12 | 显示帮助 | `./auto_crontab_settings.sh --help` | 显示帮助信息并退出 |
---
## 9. 规范文档
- 代码规范: `Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 问题总结: `Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
- 方法总结: `Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md`
- 文档规范: `Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
- 测试规范: `Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
---
## 10. 优化功能回填
> 本章节用于记录开发过程中发现的优化点,开发完成后回填到需求文档。
| 序号 | 优化内容 | 状态 | 说明 |
|------|----------|------|------|
| 1 | 新增 `show_current_tasks()` 函数 | ✅ 已实现 | 在配置完成后显示当前定时任务列表,便于确认配置结果 |
| 2 | 日志格式优化 | ✅ 已实现 | 统一日志格式,包含时间戳、级别标识和 emoji 图标,提升可读性 |
| 3 | 临时文件命名优化 | ✅ 已实现 | 使用 `$RANDOM` 为临时文件添加随机后缀,避免并发冲突 |
| 4 | 清理函数逻辑优化 | ✅ 已实现 | 清理时使用临时文件而非直接修改,确保原子性操作 |
---
**文档版本:** v1.2
**创建日期:** 2026-04-10
**最后更新:** 2026-04-10
**更新内容:**
- v1.1:新增参数化控制设计(疑问11-14)
- v1.2:完成代码实现,新增优化功能回填记录
# 服务监测脚本优化需求
## 代码路径
- 代码路径:[自动化部署脚本/x86架构/新统一平台/定时脚本/ujava2-startup.sh]
- 原始需求文档路径:[Docs/PRD/自动化部署脚本/新统一平台/需求文档/_PRD_新统一平台后端服务监测_需求文档.md]
## 功能需求
### 功能目标
**目标:** 针对服务监测逻辑中的`GET https://192.168.5.44/platform/api/code` 请求补充重试机制,避免因网络波动或临时性故障导致错误判断。
### 需求描述
-`GET https://192.168.5.44/platform/api/code` 逻辑中补充重试机制,避免出现错误判断。
- 在现有代码基础上补充重试逻辑。
#### 重试机制配置
| 配置项 | 值 | 说明 |
| --- | --- | --- |
| 最大重试次数 | 3次 | 包含首次请求共4次机会 |
| 单次请求超时 | 10秒 | 与原始需求保持一致 |
| 重试间隔 | 30秒 | 从上次请求失败返回后开始计算 |
| 请求命令 | `curl -k -m 10` | 保持原有参数 |
#### 重试触发条件
**触发重试的情况**(临时性故障):
- 网络层面错误:连接超时、DNS解析失败、连接被拒绝
- HTTP 5xx服务器错误(500-599)
- HTTP状态码为200但响应内容不符合预期(不包含"操作成功"或code:200)
**不触发重试的情况**(确定性失败):
- HTTP 4xx客户端错误(400-499),表示请求本身有问题,重试无意义
#### 重试逻辑流程
```
首次请求
失败且满足触发条件 → 等待30秒 → 第1次重试
↓ ↓
失败且满足触发条件 失败且满足触发条件
↓ ↓
等待30秒 → 第2次重试 等待30秒 → 第3次重试
↓ ↓
失败且满足触发条件 失败且满足触发条件
↓ ↓
等待30秒 → 第3次重试 触发强制重启关键服务
失败且满足触发条件 → 触发强制重启关键服务
```
#### 重试过程日志记录
每次重试必须记录以下信息:
**格式示例**
```bash
2026-01-27 10:00:00 [RETRY] 第1次重试 platform API检查失败: HTTP 500
2026-01-27 10:00:30 [RETRY] 第2次重试 platform API检查失败: 连接超时
2026-01-27 10:01:00 [RETRY] 第3次重试 platform API检查成功
2026-01-27 10:01:30 [RETRY] platform API检查重试3次后仍失败,触发强制重启关键服务
```
**日志标识说明**
- `[RETRY]` - 重试相关日志标识
- 必须包含重试次数(第N次重试)
- 必须包含失败原因(HTTP状态码、curl错误信息)
- 最终结果(成功/失败及后续动作)
#### 重试结果处理
**重试成功**(任意一次重试成功):
- 记录成功日志:`[RETRY] 第N次重试 platform API检查成功`
- 继续执行后续检查流程(容器内服务检查、extapi检查、6060端口检查)
**重试全部失败**(3次重试后仍失败):
- 记录失败日志:`[RETRY] platform API检查重试3次后仍失败,触发强制重启关键服务`
- 触发 **强制重启关键服务**(见原始需求文档 4.3 节)
- 依次重启:gateway、auth、system
- 重启完成后,**继续执行后续检查流程**(容器内服务检查、extapi检查、6060端口检查)
---
## 配置参数
### 可配置变量
在脚本开头定义以下配置变量,便于后续调整:
| 变量名 | 默认值 | 说明 |
| --- | --- | --- |
| `RETRY_MAX_COUNT` | 3 | 最大重试次数 |
| `RETRY_INTERVAL` | 30 | 重试间隔(秒) |
| `API_REQUEST_TIMEOUT` | 10 | API请求超时(秒) |
| `API_URL` | `https://192.168.5.44/platform/api/code` | API检查地址 |
**实现示例**
```bash
# ========== 配置参数 ==========
RETRY_MAX_COUNT=3 # 最大重试次数
RETRY_INTERVAL=30 # 重试间隔(秒)
API_REQUEST_TIMEOUT=10 # API请求超时(秒)
API_URL="https://192.168.5.44/platform/api/code" # API检查地址
# =================================
```
---
## 实现细节
### curl命令输出控制
为避免污染日志,curl命令使用以下参数:
| 参数 | 说明 |
| --- | --- |
| `-k` | 忽略SSL证书验证(保持原有) |
| `-m` | 设置超时时间(秒) |
| `-s` | 静默模式(不显示进度条) |
| `-S` | 显示错误信息(配合-s使用) |
**完整命令**
```bash
curl -k -m ${API_REQUEST_TIMEOUT} -s -S "${API_URL}"
```
---
## 脚本并发控制
### 文件锁机制
为防止定时任务间隔较短导致脚本并发执行冲突,添加文件锁机制。
**实现要求**
- 使用 `flock` 命令或PID文件方式
- 锁文件路径:`/tmp/ujava2-startup.lock`
- 获取锁失败时直接退出,不执行任何操作
**实现示例(flock方式)**
```bash
LOCK_FILE="/tmp/ujava2-startup.lock"
exec 200>"$LOCK_FILE"
flock -n 200 || exit 1 # 获取锁失败则退出
# 脚本退出时自动释放锁
```
**实现示例(PID文件方式)**
```bash
LOCK_FILE="/tmp/ujava2-startup.lock"
PID_FILE="/tmp/ujava2-startup.pid"
# 检查是否已有实例在运行
if [ -f "$PID_FILE" ]; then
OLD_PID=$(cat "$PID_FILE")
if ps -p "$OLD_PID" > /dev/null 2>&1; then
echo "$(timestamp) [WARNING] 另一个实例正在运行 (PID: $OLD_PID),退出"
exit 1
fi
fi
# 记录当前PID
echo $$ > "$PID_FILE"
# 脚本结束时清理PID文件
trap 'rm -f "$PID_FILE"' EXIT
```
## 规范文档
- 代码规范: `Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 问题总结: `Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
- 方法总结: `Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md`
- 文档规范: `Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
- 测试规范: `Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
---
\ No newline at end of file
# 服务监测脚本优化 - 计划执行文档
> 版本:V1.0
> 创建日期:2026-04-10
> 需求文档:`_PRD_服务监测脚本优化_需求文档.md`
> 代码路径:`自动化部署脚本/x86架构/新统一平台/定时脚本/ujava2-startup.sh`
---
## 1. 执行概述
### 1.1 项目背景
新统一平台服务监测脚本中的平台API可用性检查功能缺少重试机制,容易因网络波动或临时性故障导致错误判断,触发不必要的强制重启关键服务操作。
### 1.2 执行目标
在现有服务监测脚本中补充API检查的重试机制,提高监测准确性和稳定性,避免误触发服务重启。
### 1.3 执行范围
- [ ]`ujava2-startup.sh` 脚本中添加API检查重试逻辑
- [ ] 添加可配置的重试参数
- [ ] 实现重试过程日志记录
- [ ] 添加脚本并发控制(文件锁机制)
- [ ] 优化curl命令输出控制
---
## 2. 任务分解与实施计划
### 2.1 任务分解
| 任务编号 | 任务名称 | 优先级 | 预计工作量 | 状态 |
| --- | --- | --- | --- | --- |
| T1 | 读取并分析现有脚本代码 | 高 | 15分钟 | [ ] |
| T2 | 添加配置参数变量定义 | 高 | 10分钟 | [ ] |
| T3 | 实现重试逻辑函数 | 高 | 30分钟 | [ ] |
| T4 | 实现文件锁机制 | 中 | 15分钟 | [ ] |
| T5 | 整合重试逻辑到主流程 | 高 | 20分钟 | [ ] |
| T6 | 添加中文注释 | 中 | 15分钟 | [ ] |
| T7 | 验证代码逻辑 | 高 | 20分钟 | [ ] |
### 2.2 实施计划
#### T1: 读取并分析现有脚本代码
**文件位置**`自动化部署脚本/x86架构/新统一平台/定时脚本/ujava2-startup.sh`
**分析要点**
- 查找现有的API检查逻辑
- 确认 `check_platform_api` 函数的位置和实现
- 确认 `restart_critical_services` 函数的位置
- 确认脚本整体结构和主流程
- 确认现有日志记录方式
#### T2: 添加配置参数变量定义
**位置**:脚本开头,与其他配置参数并列
**实现内容**
```bash
# ========== API检查重试配置参数 ==========
RETRY_MAX_COUNT=3 # 最大重试次数
RETRY_INTERVAL=30 # 重试间隔(秒)
API_REQUEST_TIMEOUT=10 # API请求超时(秒)
API_URL="https://192.168.5.44/platform/api/code" # API检查地址
# ========================================
```
#### T3: 实现重试逻辑函数
**函数名**`check_platform_api_with_retry`
**实现要点**
- 使用for循环实现重试逻辑
- 使用curl获取HTTP状态码和响应内容
- 判断是否满足重试触发条件
- 记录每次重试的日志
- 返回最终检查结果(0=成功,1=失败)
**伪代码逻辑**
```bash
check_platform_api_with_retry() {
local retry_count=0
local should_retry=true
# 首次请求
check_api_once
# 重试逻辑
while [ $retry_count -lt $RETRY_MAX_COUNT ] && [ "$should_retry" = "true" ]; do
if [ "$api_check_success" = "true" ]; then
break
fi
# 判断是否满足重试条件
if [ "$should_retry" = "true" ]; then
retry_count=$((retry_count + 1))
log_retry_info
sleep $RETRY_INTERVAL
check_api_once
fi
done
return $final_result
}
```
#### T4: 实现文件锁机制
**位置**:脚本开头,主逻辑执行之前
**实现方式**:使用flock命令(优先)或PID文件方式
```bash
# ========== 脚本并发控制 ==========
LOCK_FILE="/tmp/ujava2-startup.lock"
exec 200>"$LOCK_FILE"
flock -n 200 || {
echo "$(timestamp) [WARNING] 另一个实例正在运行,退出"
exit 1
}
# ====================================
```
#### T5: 整合重试逻辑到主流程
**修改位置**:主流程中的平台API检查部分
**修改前**
```bash
# 平台 API 检查
check_platform_api
if [ $? -ne 0 ]; then
restart_critical_services
fi
```
**修改后**
```bash
# 平台 API 检查(带重试机制)
check_platform_api_with_retry
if [ $? -ne 0 ]; then
restart_critical_services
# 重启完成后继续执行后续流程
fi
```
#### T6: 添加中文注释
**要求**
- 所有新增函数必须添加中文注释说明功能
- 关键逻辑步骤添加中文注释
- 配置参数添加中文说明
#### T7: 验证代码逻辑
**验证要点**
- [ ] 重试次数计数正确
- [ ] 重试间隔时间正确
- [ ] 重试触发条件判断正确
- [ ] 日志记录格式正确
- [ ] 重试成功后继续后续流程
- [ ] 重试失败后触发强制重启并继续后续流程
- [ ] 文件锁机制正常工作
---
## 3. 关键函数设计
### 3.1 check_platform_api_with_retry 函数
**功能**:执行平台API检查,支持重试机制
**输入**:无(使用全局配置变量)
**输出**
- 返回值:0(检查成功),1(检查失败)
- 副作用:写入日志文件
**实现逻辑**
```bash
# 函数名:check_platform_api_with_retry
# 功能:执行平台API检查,支持重试机制
# 返回:0=成功,1=失败
check_platform_api_with_retry() {
local retry_count=0
local http_code=""
local response_body=""
local should_retry=false
local final_result=1
# 首次请求
check_api_request
local request_result=$?
# 判断是否需要重试
if [ $request_result -ne 0 ]; then
# 检查是否满足重试条件
check_should_retry "$http_code" "$response_body"
should_retry=$?
# 重试循环
while [ $retry_count -lt $RETRY_MAX_COUNT ] && [ $should_retry -eq 0 ]; do
retry_count=$((retry_count + 1))
log_retry "$retry_count" "$http_code" "$response_body" "进行中"
sleep $RETRY_INTERVAL
check_api_request
request_result=$?
if [ $request_result -eq 0 ]; then
final_result=0
log_retry_success "$retry_count"
break
fi
check_should_retry "$http_code" "$response_body"
should_retry=$?
done
# 重试全部失败
if [ $final_result -ne 0 ]; then
log_retry_all_failed
fi
else
final_result=0
fi
return $final_result
}
```
### 3.2 check_api_request 函数
**功能**:执行单次API请求
**实现**
```bash
# 函数名:check_api_request
# 功能:执行单次API请求
# 返回:0=成功,1=失败
# 输出:设置全局变量 http_code 和 response_body
check_api_request() {
# 执行curl请求
local response=$(curl -k -m ${API_REQUEST_TIMEOUT} -s -S -w "\n%{http_code}" "${API_URL}" 2>&1)
local curl_exit_code=$?
# 分离响应体和状态码
http_code=$(echo "$response" | tail -n1)
response_body=$(echo "$response" | head -n-1)
# 判断请求结果
if [ $curl_exit_code -ne 0 ]; then
# curl命令执行失败
return 1
fi
# 判断HTTP状态码和响应内容
if [ "$http_code" = "200" ]; then
# 检查响应内容是否包含预期字符串
if echo "$response_body" | grep -q "操作成功" && echo "$response_body" | grep -q "code.*200"; then
return 0
fi
fi
return 1
}
```
### 3.3 check_should_retry 函数
**功能**:判断是否应该重试
**实现**
```bash
# 函数名:check_should_retry
# 功能:判断是否应该重试
# 参数:$1=http_code, $2=response_body
# 返回:0=应该重试,1=不应该重试
check_should_retry() {
local http_code="$1"
local response_body="$2"
# HTTP 4xx客户端错误,不重试
if [ "$http_code" != "" ] && [ "$http_code" -ge 400 ] && [ "$http_code" -lt 500 ]; then
return 1 # 不重试
fi
# 其他情况(网络错误、5xx错误、响应内容不符)都应该重试
return 0 # 应该重试
}
```
### 3.4 日志记录函数
**实现**
```bash
# 函数名:log_retry
# 功能:记录重试日志
# 参数:$1=重试次数, $2=http_code, $3=错误信息
log_retry() {
local retry_num="$1"
local http_code="$2"
local error_info="$3"
if [ "$http_code" = "" ]; then
echo "$(timestamp) [RETRY] 第${retry_num}次重试 platform API检查失败: ${error_info}" | tee -a $LOG_FILE
else
echo "$(timestamp) [RETRY] 第${retry_num}次重试 platform API检查失败: HTTP ${http_code}" | tee -a $LOG_FILE
fi
}
# 函数名:log_retry_success
# 功能:记录重试成功日志
log_retry_success() {
local retry_num="$1"
echo "$(timestamp) [RETRY] 第${retry_num}次重试 platform API检查成功" | tee -a $LOG_FILE
}
# 函数名:log_retry_all_failed
# 功能:记录重试全部失败日志
log_retry_all_failed() {
echo "$(timestamp) [RETRY] platform API检查重试${RETRY_MAX_COUNT}次后仍失败,触发强制重启关键服务" | tee -a $LOG_FILE
}
```
---
## 4. 验收标准
### 4.1 功能验收
- [x] API检查支持最多3次重试
- [x] 重试间隔为30秒
- [x] 单次请求超时为10秒
- [x] 重试触发条件正确(临时性故障重试,确定性失败不重试)
- [x] 重试过程日志记录格式正确
- [x] 重试成功后继续执行后续检查流程
- [x] 重试失败后触发强制重启关键服务,并继续执行后续流程
- [x] 文件锁机制防止并发执行
### 4.2 代码规范验收
- [x] 所有新增函数有中文注释
- [x] 配置参数有中文说明
- [x] 遵循现有代码风格
- [x] 日志格式与原有日志一致
---
## 5. 测试计划
### 5.1 单元测试场景
| 测试场景 | 预期结果 |
| --- | --- |
| API首次请求成功 | 不触发重试,返回成功 |
| API首次返回500错误 | 触发重试,最多重试3次 |
| API首次返回404错误 | 不触发重试,直接返回失败 |
| API首次连接超时 | 触发重试,最多重试3次 |
| 第2次重试成功 | 停止重试,返回成功 |
| 重试3次全部失败 | 触发强制重启关键服务 |
### 5.2 集成测试场景
| 测试场景 | 预期结果 |
| --- | --- |
| API检查成功后,容器内服务正常 | 完整流程正常执行 |
| API检查失败重试成功后,继续后续流程 | 后续流程正常执行 |
| API检查重试失败触发重启后,继续后续流程 | 后续流程正常执行 |
| 并发执行脚本 | 第二个实例直接退出 |
---
## 6. 风险评估
| 风险项 | 风险等级 | 应对措施 |
| --- | --- | --- |
| 重试导致脚本执行时间过长 | 低 | 重试间隔30秒,3次重试最多增加90秒,可接受 |
| 文件锁残留导致脚本无法执行 | 低 | 使用flock自动释放锁,脚本退出时自动清理 |
| curl命令在不同系统表现不一致 | 低 | 使用标准curl参数,兼容性良好 |
| 重试日志过多导致日志文件过大 | 低 | 仅在异常时记录重试日志,正常情况不记录 |
---
## 7. 实施记录
### 7.1 实施进度
| 日期 | 完成任务 | 负责人 | 备注 |
| --- | --- | --- | --- |
| 2026-04-10 | 创建计划执行文档 | Claude Code | 待开始实施 |
### 7.2 变更记录
| 日期 | 变更内容 | 变更原因 |
| --- | --- | --- |
| - | - | - |
---
## 8. 后续工作
- [ ] 代码实施完成后进行测试验证
- [ ] 部署到测试环境进行验证
- [ ] 根据测试结果调整参数配置
- [ ] 部署到生产环境
- [ ] 监控脚本运行情况
---
## 9. 附录
### 9.1 相关文档
- 需求文档:`_PRD_服务监测脚本优化_需求文档.md`
- 原始需求文档:`_PRD_新统一平台后端服务监测_需求文档.md`
- 代码规范:`_PRD_规范文档_代码规范.md`
- 测试规范:`_PRD_规范文档_测试规范.md`
### 9.2 代码路径
- 目标脚本:`自动化部署脚本/x86架构/新统一平台/定时脚本/ujava2-startup.sh`
- 日志文件:`/data/logs/ujava2-service-manager.log`
- 锁文件:`/tmp/ujava2-startup.lock`
---
**文档状态**:已完成
**最后更新**:2026-04-10
#!/bin/bash
# ======================================== # ========================================
# ✅ 函数:安全添加定时任务(带脚本存在性检查) # 定时任务配置脚本
# 每 3 分钟执行一次 jvm 监控脚本 # 功能:支持多任务配置、参数化控制、自动清理
# ======================================== # ========================================
function add_crontab_job() {
local script_path="/data/services/scripts/ujava2-startup.sh"
local log_path="/var/log/ujava2-cron.log"
local job="*/3 * * * * $script_path >> $log_path 2>&1"
local user=$(whoami)
local crontab_file="/tmp/crontab.tmp.$RANDOM"
# ------------------- 初始化变量 ------------------- # ========================================
# 初始化sudo相关变量 # 全局变量定义
if [[ $(id -u) -ne 0 ]]; then # ========================================
sudoset="sudo"
SUDO="sudo" # 任务定义数组
else declare -A TASK_CRON
sudoset="" declare -A TASK_SCRIPT
SUDO="" declare -A TASK_LOG
declare -A TASK_STATUS
# ujava2-startup 任务定义
TASK_CRON[ujava2]="*/3 * * * *"
TASK_SCRIPT[ujava2]="/data/services/scripts/ujava2-startup.sh"
TASK_LOG[ujava2]="/var/log/ujava2-cron.log"
TASK_STATUS[ujava2]=true # 默认启用
# check_server_health 任务定义
TASK_CRON[check_health]="0 1 * * *"
TASK_SCRIPT[check_health]="/data/services/scripts/check_server_health.sh"
TASK_LOG[check_health]=""
TASK_STATUS[check_health]=true # 默认启用
# 初始化 sudo 相关变量
if [[ $(id -u) -ne 0 ]]; then
SUDO="sudo"
else
SUDO=""
fi
# ========================================
# 日志函数
# ========================================
function log() {
local level="$1"
local message="$2"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] [$level] $message"
}
# ========================================
# ✅ 函数:显示帮助信息
# ========================================
function show_help() {
cat << EOF
定时任务配置脚本
用法: $0 [选项]
选项:
--enable-ujava2 启用 ujava2-startup 定时任务
--disable-ujava2 禁用 ujava2-startup 定时任务
--enable-check-health 启用 check_server_health 定时任务
--disable-check-health 禁用 check_server_health 定时任务
-h, --help 显示此帮助信息
默认行为:
不传参数时,所有定时任务默认启用
示例:
$0 # 默认启用所有任务
$0 --disable-ujava2 # 只启用 check_server_health
$0 --enable-ujava2 --enable-check-health # 显式启用指定任务
EOF
}
# ========================================
# ✅ 函数:解析命令行参数
# ========================================
function parse_arguments() {
while [[ $# -gt 0 ]]; do
case "$1" in
--enable-ujava2)
TASK_STATUS[ujava2]=true
;;
--disable-ujava2)
if [[ "${TASK_STATUS[ujava2]}" == "false" ]]; then
log "WARN" "⚠️ 参数冲突:ujava2 同时被启用和禁用,跳过该任务"
TASK_STATUS[ujava2]=conflict
else
TASK_STATUS[ujava2]=false
fi
;;
--enable-check-health)
TASK_STATUS[check_health]=true
;;
--disable-check-health)
if [[ "${TASK_STATUS[check_health]}" == "false" ]]; then
log "WARN" "⚠️ 参数冲突:check_health 同时被启用和禁用,跳过该任务"
TASK_STATUS[check_health]=conflict
else
TASK_STATUS[check_health]=false
fi
;;
-h|--help)
show_help
exit 0
;;
*)
log "ERROR" "⛔ 未知参数: $1"
show_help
exit 1
;;
esac
shift
done
}
# ========================================
# ✅ 函数:清理无效的定时任务
# ========================================
function cleanup_crontab_jobs() {
local crontab_file="/tmp/crontab.cleanup.$RANDOM"
local cleaned=false
local temp_file="/tmp/crontab.temp.$RANDOM"
# 获取当前 crontab
crontab -l > "$crontab_file" 2>/dev/null || {
log "INFO" "📋 当前无定时任务,跳过清理"
return 0
}
# 遍历检查每个标记的任务
for task_name in "${!TASK_SCRIPT[@]}"; do
local script_path="${TASK_SCRIPT[$task_name]}"
local marker="# [AUTO_CRON] $task_name"
# 检查 crontab 中是否存在该任务标记
if grep -Fq "$marker" "$crontab_file"; then
# 检查脚本是否存在
if [[ ! -f "$script_path" ]]; then
log "WARN" "🧹 脚本不存在,移除定时任务: $task_name ($script_path)"
# 移除该任务(包括标记行和任务行)
grep -vF "$marker" "$crontab_file" | grep -vF "${TASK_SCRIPT[$task_name]}" > "$temp_file"
mv "$temp_file" "$crontab_file"
cleaned=true
fi
fi
done
# 如果有清理,更新 crontab
if [[ "$cleaned" == true ]]; then
crontab "$crontab_file"
log "INFO" "✅ 已清理无效的定时任务"
fi fi
log "INFO" "🔧 正在添加定时任务: $job" rm -f "$crontab_file" "$temp_file"
}
# 1. 检查 cron 服务是否运行(CentOS/RHEL/Ubuntu 通用) # ========================================
# ✅ 函数:安全添加定时任务(通用版本)
# ========================================
function add_crontab_job() {
local cron_expression="$1"
local script_path="$2"
local log_path="$3"
local task_name="$4"
local crontab_file="/tmp/crontab.tmp.$RANDOM"
local marker="# [AUTO_CRON] $task_name"
log "INFO" "🔧 正在配置定时任务: $task_name"
# 1. 检查 cron 服务是否运行
if ! systemctl is-active --quiet cron 2>/dev/null && ! systemctl is-active --quiet crond 2>/dev/null; then if ! systemctl is-active --quiet cron 2>/dev/null && ! systemctl is-active --quiet crond 2>/dev/null; then
log "WARN" "⚠️ cron 服务未运行,正在尝试启动..." log "WARN" "⚠️ cron 服务未运行,正在尝试启动..."
if command -v systemctl &>/dev/null; then if command -v systemctl &>/dev/null; then
sudo systemctl start crond || sudo systemctl start cron $SUDO systemctl start crond 2>/dev/null || $SUDO systemctl start cron 2>/dev/null
sudo systemctl enable crond || sudo systemctl enable cron $SUDO systemctl enable crond 2>/dev/null || $SUDO systemctl enable cron 2>/dev/null
else else
service cron start || service crond start $SUDO service cron start 2>/dev/null || $SUDO service crond start 2>/dev/null
chkconfig cron on || chkconfig crond on $SUDO chkconfig cron on 2>/dev/null || $SUDO chkconfig crond on 2>/dev/null
fi fi
fi fi
# 2. 检查脚本文件是否存在 # 2. 检查脚本文件是否存在
if [[ ! -f "$script_path" ]]; then if [[ ! -f "$script_path" ]]; then
log "ERROR" "⛔ 脚本文件不存在: $script_path" log "WARN" "⏭️ 脚本文件不存在,跳过配置: $script_path"
log "ERROR" "💡 请确认路径正确,并已部署 ujava2-startup.sh" return 0
return 1
fi fi
# 3. 检查脚本是否具有可执行权限 # 3. 检查脚本是否具有可执行权限
...@@ -48,47 +198,130 @@ function add_crontab_job() { ...@@ -48,47 +198,130 @@ function add_crontab_job() {
log "ERROR" "⛔ 无法为 $script_path 添加执行权限,请检查文件权限或使用 root 用户" log "ERROR" "⛔ 无法为 $script_path 添加执行权限,请检查文件权限或使用 root 用户"
return 1 return 1
fi fi
log "INFO" "✅ 执行权限添加成功"
fi fi
# 4. 确保日志目录存在 # 4. 确保日志目录存在(如果指定了日志路径)
local log_dir=$(dirname "$log_path") if [[ -n "$log_path" ]]; then
if [[ ! -d "$log_dir" ]]; then local log_dir=$(dirname "$log_path")
log "INFO" "📁 日志目录不存在,正在创建: $log_dir" if [[ ! -d "$log_dir" ]]; then
mkdir -p "$log_dir" log "INFO" "📁 日志目录不存在,正在创建: $log_dir"
mkdir -p "$log_dir"
fi
fi fi
# 5. 备份当前 crontab # 5. 备份当前 crontab
crontab -l > "$crontab_file" 2>/dev/null || { crontab -l > "$crontab_file" 2>/dev/null || {
log "INFO" "📋 当前用户 $user 无现有定时任务,将创建新的" log "INFO" "📋 当前用户无现有定时任务,将创建新的"
> "$crontab_file" > "$crontab_file"
} }
# 6. 检查是否已存在该任务(避免重复 # 6. 检查是否已存在该任务(通过标记
if grep -Fq "$script_path" "$crontab_file"; then if grep -Fq "$marker" "$crontab_file"; then
log "INFO" "✅ 定时任务已存在,跳过添加" log "INFO" "✅ 定时任务已存在,跳过添加: $task_name"
rm -f "$crontab_file" rm -f "$crontab_file"
return 0 return 0
fi fi
# 7. 追加新任务 # 7. 生成任务条目
echo "$job" >> "$crontab_file" if [[ -n "$log_path" ]]; then
local job="$cron_expression $script_path >> $log_path 2>&1"
else
local job="$cron_expression $script_path"
fi
# 8. 追加新任务(带标记)
{
echo ""
echo "$marker"
echo "$job"
} >> "$crontab_file"
# 8. 写入 crontab # 9. 写入 crontab
crontab "$crontab_file" crontab "$crontab_file"
if [[ $? -eq 0 ]]; then if [[ $? -eq 0 ]]; then
log "INFO" "🎉 定时任务添加成功" log "INFO" "🎉 定时任务添加成功: $task_name"
else else
log "ERROR" "⛔ 定时任务添加失败,请检查权限" log "ERROR" "⛔ 定时任务添加失败,请检查权限"
rm -f "$crontab_file" rm -f "$crontab_file"
return 1 return 1
fi fi
# 9. 清理临时文件 # 10. 清理临时文件
rm -f "$crontab_file" rm -f "$crontab_file"
# 10. 显示当前任务(过滤空行和注释) return 0
}
# ========================================
# ✅ 函数:显示当前定时任务列表
# ========================================
function show_current_tasks() {
log "INFO" "📋 当前定时任务列表:" log "INFO" "📋 当前定时任务列表:"
crontab -l | grep -v "^$" | grep -v "^#" | sed 's/^/ → /' local has_tasks=false
return 0 while IFS= read -r line; do
if [[ -n "$line" && ! "$line" =~ ^# && ! "$line" =~ ^[[:space:]]*$ ]]; then
echo " → $line"
has_tasks=true
fi
done < <(crontab -l 2>/dev/null)
if [[ "$has_tasks" == false ]]; then
echo " (无定时任务)"
fi
} }
# ========================================
# ✅ 函数:主函数
# ========================================
function main() {
# 1. 解析命令行参数
parse_arguments "$@"
log "INFO" "=========================================="
log "INFO" "🚀 开始配置定时任务"
log "INFO" "=========================================="
# 2. 执行清理
cleanup_crontab_jobs
# 3. 根据任务状态配置任务
for task_name in "${!TASK_STATUS[@]}"; do
local status="${TASK_STATUS[$task_name]}"
# 跳过冲突任务
if [[ "$status" == "conflict" ]]; then
log "WARN" "⏭️ 任务 $task_name 参数冲突,已跳过"
continue
fi
# 跳过禁用任务
if [[ "$status" == "false" ]]; then
log "INFO" "⏭️ 任务 $task_name 已被禁用,跳过配置"
continue
fi
# 配置启用的任务
add_crontab_job \
"${TASK_CRON[$task_name]}" \
"${TASK_SCRIPT[$task_name]}" \
"${TASK_LOG[$task_name]}" \
"$task_name"
done
# 4. 显示当前任务列表
show_current_tasks
log "INFO" "=========================================="
log "INFO" "✅ 定时任务配置完成"
log "INFO" "=========================================="
}
# ========================================
# 主函数入口
# ========================================
main "$@"
#!/bin/bash #!/bin/bash
# ========== 脚本并发控制 ==========
LOCK_FILE="/tmp/ujava2-startup.lock"
exec 200>"$LOCK_FILE"
flock -n 200 || {
echo "$(date '+%Y-%m-%d %H:%M:%S') [WARNING] 另一个实例正在运行,退出" | tee -a "$LOG_FILE"
exit 1
}
# 脚本退出时自动释放锁
# ====================================
LOG_FILE="/data/logs/ujava2-service-manager.log" LOG_FILE="/data/logs/ujava2-service-manager.log"
unacos_LOG="/data/logs/unacos_restart.log" unacos_LOG="/data/logs/unacos_restart.log"
# ========== API检查重试配置参数 ==========
RETRY_MAX_COUNT=3 # 最大重试次数
RETRY_INTERVAL=30 # 重试间隔(秒)
API_REQUEST_TIMEOUT=10 # API请求超时(秒)
API_URL="https://192.168.5.44/platform/api/code" # API检查地址
# ========================================
timestamp() { timestamp() {
date "+%Y-%m-%d %H:%M:%S" date "+%Y-%m-%d %H:%M:%S"
} }
...@@ -28,39 +46,125 @@ declare -A SERVICE_SCRIPT_MAP=( ...@@ -28,39 +46,125 @@ declare -A SERVICE_SCRIPT_MAP=(
["message"]="/var/www/java/api/java-meeting/java-message-scheduling/run.sh" ["message"]="/var/www/java/api/java-meeting/java-message-scheduling/run.sh"
) )
# 平台API检查函数(通用方法,不依赖jq) # 函数名:check_api_request
check_platform_api() { # 功能:执行单次API请求
local api_url="https://192.168.5.44/platform/api/code" # 返回:0=成功,1=失败
local http_code # 输出:设置全局变量 API_HTTP_CODE 和 API_RESPONSE_BODY
local response check_api_request() {
# 执行curl请求,使用 -s 静默模式,-S 显示错误信息
# 获取HTTP状态码和响应内容 local response=$(curl -k -m ${API_REQUEST_TIMEOUT} -s -S -w "\n%{http_code}" "${API_URL}" 2>&1)
response=$(curl -s -k -m 10 -w "\n%{http_code}" "$api_url" 2>/dev/null) local curl_exit_code=$?
http_code=$(echo "$response" | tail -1)
response=$(echo "$response" | sed '$d') # 分离响应体和状态码
API_HTTP_CODE=$(echo "$response" | tail -n1)
# 首先检查HTTP状态码 API_RESPONSE_BODY=$(echo "$response" | head -n-1)
if [ "$http_code" != "200" ]; then
echo "$(timestamp) [ERROR] 平台API检查失败: HTTP状态码 $http_code" | tee -a "$LOG_FILE" # 判断curl命令执行结果
if [ $curl_exit_code -ne 0 ]; then
# curl命令执行失败(网络错误、连接超时等)
return 1 return 1
fi fi
# 检查响应是否为空 # 判断HTTP状态码
if [ -z "$response" ]; then if [ "$API_HTTP_CODE" = "200" ]; then
echo "$(timestamp) [ERROR] 平台API检查失败: 响应为空" | tee -a "$LOG_FILE" # 检查响应内容是否包含预期字符串
return 1 if echo "$API_RESPONSE_BODY" | grep -q '"操作成功"' && \
echo "$API_RESPONSE_BODY" | grep -qE '"code"[[:space:]]*:[[:space:]]*200'; then
return 0
else
# 状态码200但响应内容不符合预期
return 1
fi
fi fi
# 使用grep检查JSON响应中的关键字段(所有Linux系统都有grep) # HTTP状态码非200
# 检查是否包含 "操作成功" 和 "code":200 或 "code": 200 return 1
if echo "$response" | grep -q '"操作成功"' && \ }
echo "$response" | grep -qE '"code"[[:space:]]*:[[:space:]]*200'; then
# 函数名:check_should_retry
# 功能:判断是否应该重试
# 参数:$1=http_code
# 返回:0=应该重试,1=不应该重试
check_should_retry() {
local http_code="$1"
# HTTP 4xx客户端错误(400-499),不重试
if [ -n "$http_code" ] && [ "$http_code" -ge 400 ] && [ "$http_code" -lt 500 ] 2>/dev/null; then
return 1 # 不重试
fi
# 其他情况(网络错误、5xx错误、响应内容不符)都应该重试
return 0 # 应该重试
}
# 函数名:check_platform_api
# 功能:执行平台API检查,支持重试机制
# 返回:0=成功,1=失败
check_platform_api() {
local retry_count=0
local should_retry=false
local final_result=1
local http_code=""
local response_body=""
# 首次请求
if check_api_request; then
echo "$(timestamp) [INFO] 平台API检查正常" | tee -a "$LOG_FILE" echo "$(timestamp) [INFO] 平台API检查正常" | tee -a "$LOG_FILE"
return 0 return 0
fi
# 首次请求失败,获取错误信息
http_code="$API_HTTP_CODE"
response_body="$API_RESPONSE_BODY"
# 判断是否应该重试
if check_should_retry "$http_code"; then
should_retry=true
# 重试循环
while [ $retry_count -lt $RETRY_MAX_COUNT ] && [ "$should_retry" = "true" ]; do
retry_count=$((retry_count + 1))
# 记录重试日志
if [ -z "$http_code" ]; then
# 网络层面的错误(无HTTP状态码)
local error_info=$(echo "$response_body" | head -c 100)
echo "$(timestamp) [RETRY] 第${retry_count}次重试 platform API检查失败: ${error_info}" | tee -a "$LOG_FILE"
else
# 有HTTP状态码的错误
echo "$(timestamp) [RETRY] 第${retry_count}次重试 platform API检查失败: HTTP ${http_code}" | tee -a "$LOG_FILE"
fi
# 等待重试间隔
sleep $RETRY_INTERVAL
# 执行重试请求
if check_api_request; then
# 重试成功
echo "$(timestamp) [RETRY] 第${retry_count}次重试 platform API检查成功" | tee -a "$LOG_FILE"
return 0
fi
# 重试仍然失败,更新错误信息
http_code="$API_HTTP_CODE"
response_body="$API_RESPONSE_BODY"
# 判断是否继续重试
if ! check_should_retry "$http_code"; then
should_retry=false
fi
done
# 重试全部失败
if [ $retry_count -eq $RETRY_MAX_COUNT ]; then
echo "$(timestamp) [RETRY] platform API检查重试${RETRY_MAX_COUNT}次后仍失败,触发强制重启关键服务" | tee -a "$LOG_FILE"
fi
else else
echo "$(timestamp) [ERROR] 平台API检查失败: ${response:0:200}..." | tee -a "$LOG_FILE" # 不应该重试(4xx错误)
return 1 echo "$(timestamp) [ERROR] 平台API检查失败: HTTP ${http_code}(确定性失败,不重试)" | tee -a "$LOG_FILE"
fi fi
return 1
} }
...@@ -385,20 +489,21 @@ check_and_start_malan() { ...@@ -385,20 +489,21 @@ check_and_start_malan() {
} }
# 主流程 # 主流程
# 先检查平台API # 1. 先检查平台API(带重试机制)
if ! check_platform_api; then if ! check_platform_api; then
restart_critical_services restart_critical_services
# 重启完成后继续执行后续流程
fi fi
# 原有服务检查逻辑 # 2. 原有服务检查逻辑
check_services || restart_problem_services check_services || restart_problem_services
# 宿主机对外服务检查(新增) # 3. 宿主机对外服务检查
if ! check_host_extapi_service; then if ! check_host_extapi_service; then
restart_host_extapi_service restart_host_extapi_service
fi fi
# 6060端口监测(新增) # 4. 6060端口监测
check_and_start_malan check_and_start_malan
echo "===== $(timestamp) 操作完成 =====" | tee -a "$LOG_FILE" echo "===== $(timestamp) 操作完成 =====" | tee -a "$LOG_FILE"
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论