基于状态机逻辑的 Linux 网络多 IP 监控系统

目录

一、 理论知识介绍:状态机监控与日志抑制策略

在编写生产环境的监控脚本时,最核心的矛盾在于​**​“监控精度”​与“日志冗余”之间的平衡。我们采用状态机(State Machine)**​的设计思想来解决这个问题。

  1. 分状态策略​:
    • ​**正常态(OK)**​:采用“心跳抑制”逻辑。脚本不记录每一次成功的探测,而是设定一个周期(如 60s),仅在此周期结束时记录一次,证明链路存活。
    • ​**异常态(FAIL)**​:采用“高敏感”逻辑。一旦探测失败,立即写入日志,不设任何等待时间。
  2. ​**状态恢复的即时性(关键点)**​:
    • 当监控目标从 FAIL 恢复到 OK 时,必须立即打破 60s 的心跳周期,记录第一条恢复日志。这是通过在失败分支中重置状态计数器来实现的。
  3. 日志生命周期管理​:
    • 脚本应具备自愈能力,通过监测文件字节大小(Byte Size)实现日志轮转,防止因长期运行占满磁盘空间(Root Partition Full)。

二、 单步命令实践验证

在合成完整脚本前,我们可以通过以下原子命令验证核心技术点:

1. 验证网络状态码

使用 $? 变量捕获上一条命令的退出状态。

# 测试通畅 IP,返回 0
ping -c 1 -W 1 8.8.8.8 > /dev/null 2>&1; echo $?

# 测试不通 IP,返回非 0(通常为 1 或 2)
ping -c 1 -W 1 10.255.255.1 > /dev/null 2>&1; echo $?

2. 验证时间戳差值计算

利用 Unix 时间戳(Seconds since Epoch)进行逻辑判断。

# 获取当前秒数
NOW=$(date +%s)
# 模拟 70 秒前的时间点
LAST=$((NOW - 70))
# 判断间隔是否超过 60 秒
[[ $((NOW - LAST)) -ge 60 ]] && echo "触发心跳记录"

3. 验证日志轮转动作

通过 stat 获取文件属性并进行重命名。

# 模拟一个 1MB 的日志文件检查
# -c%s 仅输出字节大小
if [ $(stat -c%s "/var/log/ping.log") -ge 1048576 ]; then
    mv /var/log/ping.log /var/log/ping.log.old
    touch /var/log/ping.log
fi

三、 完整版代码实现(单进程稳健版)

#!/bin/bash

# ==========================
# 配置与初始化
# ==========================
IPS=("8.8.8.8" "1.1.1.1" "192.168.1.1")
INTERVAL=5              # 探测频率 (秒)
SUCCESS_LOG_INT=60      # 成功状态记录间隔 (秒)
LOG_FILE="/var/log/multi_ping.log"
MAX_SIZE=$((1024*1024)) # 1MB 轮转阈值

declare -A LAST_SUCCESS_LOG  # 记录每个 IP 上次成功记录的时间戳

# ==========================
# 主循环逻辑
# ==========================
while true; do
    # 1. 自动日志轮转
    if [ -f "$LOG_FILE" ] && [ $(stat -c%s "$LOG_FILE") -ge $MAX_SIZE ]; then
        mv "$LOG_FILE" "$LOG_FILE.bak"
        touch "$LOG_FILE"
    fi

    NOW=$(date +%s)
    DATE_STR=$(date '+%F %T')

    for IP in "${IPS[@]}"; do
        if ping -c 1 -W 1 "$IP" > /dev/null 2>&1; then
            # --- 成功处理:抑制逻辑 ---
            LAST=${LAST_SUCCESS_LOG[$IP]:-0}
            if (( NOW - LAST >= SUCCESS_LOG_INT )); then
                echo "[$DATE_STR] $IP OK" >> "$LOG_FILE"
                LAST_SUCCESS_LOG[$IP]=$NOW
            fi
        else
            # --- 失败处理:即时记录 ---
            echo "[$DATE_STR] $IP FAIL" >> "$LOG_FILE"
            # 关键:重置成功时间戳。确保恢复后的下一次 OK 能立刻记录。
            LAST_SUCCESS_LOG[$IP]=0
        fi
    done

    sleep "$INTERVAL"
done