204 lines
6.2 KiB
Bash
204 lines
6.2 KiB
Bash
#!/bin/bash
|
||
# ================================================
|
||
# KR数据同步后 MD5 校验与重试脚本
|
||
# 功能:对比源目录与远程目标目录的 MD5(基于相对路径),不一致则重新同步
|
||
# 用法:./kr_data_sync_verify.sh [YYYYMMDD]
|
||
# ================================================
|
||
|
||
# ---------- 配置参数 ----------
|
||
#SRC_BASE="/home/sysop/kr_md_save_pkt_loss_v5/data/stockdata" #南方
|
||
SRC_BASE="/home/sysop/kr_md_save_centos7_9_20260317/data/stockdata" #金桥
|
||
DEST_HOST="swdata@119.167.194.234"
|
||
DEST_PORT="60000"
|
||
#DEST_BASE="/mnt/data1/data_new_nf/data/stockdata" #南方
|
||
DEST_BASE="/mnt/data1/data_new/data/stockdata" #金桥
|
||
DEST_MARK_BASE="/mnt/data1/md5"
|
||
SSH_OPTS="-o ConnectTimeout=30 -o BatchMode=yes -o StrictHostKeyChecking=no -o ServerAliveInterval=60 -o ServerAliveCountMax=3"
|
||
LOG_DIR="/var/log/kr_data_sync"
|
||
LOG_FILE="$LOG_DIR/verify_$(date +%Y%m%d).log"
|
||
MAX_RETRIES=10
|
||
|
||
mkdir -p "$LOG_DIR"
|
||
|
||
# ---------- 日志函数 ----------
|
||
log_info() {
|
||
echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] $1" | tee -a "$LOG_FILE"
|
||
}
|
||
log_error() {
|
||
echo "$(date '+%Y-%m-%d %H:%M:%S') [ERROR] $1" | tee -a "$LOG_FILE"
|
||
}
|
||
|
||
usage() {
|
||
echo "用法: $0 [YYYYMMDD]"
|
||
exit 1
|
||
}
|
||
|
||
check_dependencies() {
|
||
command -v rsync &>/dev/null || { log_error "请安装 rsync"; exit 1; }
|
||
command -v md5sum &>/dev/null || { log_error "请安装 md5sum"; exit 1; }
|
||
}
|
||
|
||
check_ssh() {
|
||
log_info "检查 SSH 连接..."
|
||
ssh -p "$DEST_PORT" $SSH_OPTS "$DEST_HOST" "echo ok" &>/dev/null || {
|
||
log_error "SSH 连接失败"
|
||
exit 1
|
||
}
|
||
log_info "SSH 连接正常"
|
||
}
|
||
|
||
check_remote_md5sum() {
|
||
ssh -p "$DEST_PORT" $SSH_OPTS "$DEST_HOST" "command -v md5sum" &>/dev/null || {
|
||
log_error "远程主机缺少 md5sum"
|
||
exit 1
|
||
}
|
||
log_info "远程 md5sum 可用"
|
||
}
|
||
|
||
ensure_remote_mark_dir() {
|
||
ssh -p "$DEST_PORT" $SSH_OPTS "$DEST_HOST" "mkdir -p '$DEST_MARK_BASE'" &>/dev/null || {
|
||
log_error "无法创建远程标记目录 $DEST_MARK_BASE"
|
||
exit 1
|
||
}
|
||
log_info "远程标记目录已确认: $DEST_MARK_BASE"
|
||
}
|
||
|
||
# ========== 生成相对路径的 MD5 列表 ==========
|
||
generate_local_md5() {
|
||
local dir="$1"
|
||
local tmpfile=$(mktemp)
|
||
# 进入源目录,使用相对路径(以 ./ 开头)
|
||
(cd "$dir" && find . -type f -print0 2>/dev/null | xargs -0 md5sum 2>/dev/null | sort -k2) > "$tmpfile"
|
||
echo "$tmpfile"
|
||
}
|
||
|
||
generate_remote_md5() {
|
||
local dir="$1"
|
||
local tmpfile=$(mktemp)
|
||
# 远程进入目标目录,同样使用相对路径
|
||
ssh -p "$DEST_PORT" $SSH_OPTS "$DEST_HOST" "cd '$dir' && find . -type f -print0 2>/dev/null | xargs -0 md5sum 2>/dev/null | sort -k2" > "$tmpfile" 2>/dev/null
|
||
echo "$tmpfile"
|
||
}
|
||
# ====================================================
|
||
|
||
compare_md5() {
|
||
local local_list="$1"
|
||
local remote_list="$2"
|
||
local diff_file=$(mktemp)
|
||
|
||
if [ ! -s "$local_list" ]; then
|
||
log_error "本地文件列表为空"
|
||
rm -f "$diff_file"
|
||
return 1
|
||
fi
|
||
|
||
if diff "$local_list" "$remote_list" > "$diff_file" 2>&1; then
|
||
log_info "MD5 校验一致"
|
||
rm -f "$diff_file"
|
||
return 0
|
||
else
|
||
log_error "MD5 校验不一致,差异如下:"
|
||
cat "$diff_file" | tee -a "$LOG_FILE"
|
||
rm -f "$diff_file"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
do_sync() {
|
||
local src="$1"
|
||
local dest="$2"
|
||
log_info "开始同步: $src -> $dest"
|
||
rsync -avz --progress --delete \
|
||
-e "ssh -p $DEST_PORT $SSH_OPTS" \
|
||
"$src/" \
|
||
"$DEST_HOST:$dest/" 2>&1 | tee -a "$LOG_FILE"
|
||
local sync_result=${PIPESTATUS[0]}
|
||
if [ $sync_result -eq 0 ]; then
|
||
log_info "同步完成"
|
||
return 0
|
||
else
|
||
log_error "同步失败,退出码: $sync_result"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
create_mark_file() {
|
||
local date_str="$1"
|
||
#local remote_mark_file="$DEST_MARK_BASE/${date_str}_nf" #南方
|
||
local remote_mark_file="$DEST_MARK_BASE/${date_str}_jq" #金桥
|
||
echo "Sync completed and verified at $(date -u '+%Y-%m-%d %H:%M:%S UTC')" | \
|
||
ssh -p "$DEST_PORT" $SSH_OPTS "$DEST_HOST" "cat > '$remote_mark_file'"
|
||
if [ $? -eq 0 ]; then
|
||
log_info "已创建标记文件: $remote_mark_file"
|
||
else
|
||
log_error "创建标记文件失败"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
main() {
|
||
local target_date="$1"
|
||
[ -z "$target_date" ] && target_date=$(date +%Y%m%d)
|
||
if ! [[ "$target_date" =~ ^[0-9]{8}$ ]]; then
|
||
log_error "日期格式必须为 YYYYMMDD"
|
||
usage
|
||
fi
|
||
|
||
local src_dir="$SRC_BASE/$target_date"
|
||
local dest_dir="$DEST_BASE/$target_date"
|
||
local remote_mark_file="$DEST_MARK_BASE/$target_date"
|
||
|
||
log_info "========== 开始 MD5 校验与同步任务 =========="
|
||
log_info "日期: $target_date"
|
||
log_info "源目录: $src_dir"
|
||
log_info "目标目录: $dest_dir"
|
||
log_info "标记文件: $remote_mark_file"
|
||
|
||
[ ! -d "$src_dir" ] && { log_error "源目录不存在: $src_dir"; exit 1; }
|
||
|
||
check_dependencies
|
||
check_ssh
|
||
check_remote_md5sum
|
||
ensure_remote_mark_dir
|
||
|
||
local retry_count=0
|
||
local success=false
|
||
|
||
while [ $retry_count -lt $MAX_RETRIES ]; do
|
||
retry_count=$((retry_count + 1))
|
||
log_info "===== 第 $retry_count 次校验 ====="
|
||
|
||
local local_md5=$(generate_local_md5 "$src_dir")
|
||
local remote_md5=$(generate_remote_md5 "$dest_dir")
|
||
|
||
if compare_md5 "$local_md5" "$remote_md5"; then
|
||
success=true
|
||
rm -f "$local_md5" "$remote_md5"
|
||
break
|
||
else
|
||
rm -f "$local_md5" "$remote_md5"
|
||
log_info "MD5 不一致,执行同步..."
|
||
if ! do_sync "$src_dir" "$dest_dir"; then
|
||
log_error "同步失败,终止重试"
|
||
exit 1
|
||
fi
|
||
sleep 5
|
||
fi
|
||
done
|
||
|
||
if [ "$success" = true ]; then
|
||
log_info "经过 $retry_count 次校验,MD5 已一致"
|
||
create_mark_file "$target_date"
|
||
log_info "任务完成,已生成标记文件"
|
||
else
|
||
log_error "达到最大重试次数 $MAX_RETRIES,仍然无法使 MD5 一致"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "========== 任务结束 =========="
|
||
}
|
||
|
||
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
|
||
usage
|
||
fi
|
||
main "$1" |