省心的邮件提醒监控进程

前言

       在使用服务器运行重要的后台服务的时候,难免因为意外原因造成服务中断宕机。此时一个简单的监控进程就很有必要。监控进程脚本很简单,重点在配置邮件服务。

准备条件

本文中使用的是ubuntu22.04版系统。

正式开始

安装邮件服务

安装前先更新下系统

apt-get update

安装mailutils,出现图形选择界面直接默认下一步就行。

apt-get install -y mailutils

安装s-nail,如果出现图形选择界面也是直接默认下一步就行。

apt-get install -y s-nail

配置外部SMTP

安装好后,将如下几行加入/etc/s-nail.rc这个文件最下面

vim /etc/s-nail.rc

将下面代码粘贴进去。

set from=569157865@163.com              #设置发送邮箱
set smtp=smtps://smtp.163.com:465       #设置smtp服务器和端口
set smtp-auth-user=569157865@163.com    #设置用户名
set smtp-auth-password=IHMRRQHMAXZECVUV #授权码,不是登录密码
set smtp-auth=login                     #认证方式

测试邮件发送

echo "卧槽!牛逼!可以可以!" |s-nail -s "整两句..." 951753852@qq.com

此时打开你的邮箱,查看是否收到邮件。
不出意外主题为”整两句…”,内容为”卧槽!牛逼!可以可以!”的邮件就发过来了。

SMTP配置补充说明

本文配置的发送邮件的邮箱是163平台的。如果是你配置的是同一平台并且使用的是同一端口,smtp服务器和端口就无需修改。另外保持服务465端口开放,用哪个开放哪个就行。下面详细说明。

  • set from=569157865@163.com
    这个为发送邮箱,不需多说

  • set smtp=smtps://smtp.163.com:465

    1. 163.com
      接收邮件服务器:pop.163.com,端口:110或995(使用SSL时)
      接收邮件服务器:imap.163.com,端口:143或993(使用SSL时)
      发送邮件服务器:smtp.163.com,端口:25或465/994(使用SSL时)
    2. 126邮箱
      接收邮件服务器:pop.126.com,端口:110
      发送邮件服务器:smtp.126.com,端口:25
    3. 139邮箱
      POP3服务器地址:POP.139.com,端口:110
      SMTP服务器地址:SMTP.139.com,端口:25
    4. QQ邮箱
      接收邮件服务器:pop.qq.com,端口:110或995(使用SSL时)
      接收邮件服务器:imap.qq.com,端口:143或993(使用SSL时)
      发送邮件服务器:smtp.qq.com,端口:25或465/587(使用SSL时)
    5. Gmail (google.com)
      POP3服务器地址:pop.gmail.com,端口:995(使用SSL)
      SMTP服务器地址:smtp.gmail.com,端口:587(使用SSL)
    6. 雅虎邮箱
      接收邮件服务器:pop.mail.yahoo.cn,端口:110或995(使用SSL时)
      接收邮件服务器:imap.mail.yahoo.cn,端口:143或993(使用SSL时)
      发送邮件服务器:smtp.mail.yahoo.cn,端口:25或465(使用SSL时)
    7. HotMail
      接收邮件服务器:pop3.live.com,端口:995
      发送邮件服务器:smtp.live.com,端口:25
  • set smtp-auth-user=569157865@163.com
    跟发送邮箱保持一致就行

  • set smtp-auth-password=IHMRRQHMAXZECVUV
    授权码,需要去到平台,如图:

  • set smtp-auth=login
    此项为固定模式

发送邮件没问题的话,至此配置SMTP服务完成。可以进行下一步了。

监控脚本

在后台运行服务的同级目录新建一个MonitoringProcess.sh文件,名称随意。

touch MonitoringProcess.sh

然后将下面这段代码粘贴进去,保存退出。两种方式监测进程:(二选一)

  • 进程的完整名称
  • 进程ID

可根据自己的需要改良代码。比如监测多个进程等。

方式:1

ps -ef | grep 名称          #获取进程PID
ps -p 进程PID -o comm=      #返回完整进程名称

以上两个指令可获取需要的参数。

#!/bin/bash

while true; do
    #if pgrep 进程的完整名称 > /dev/null; then   #进程完整名称
    if ps -p 进程PID > /dev/null; then       #进程PID
    sleep 10   #每10秒查询一次。
else
    current_time=$(TZ='Asia/Shanghai' date +"%Y-%m-%d %H:%M:%S")
    echo "宕机时间:$current_time"|s-nail -s "微信的进程服务已宕机" 12345678@qq.com
    # 一旦邮件发送,终止循环以防止不断发送邮件
    break
  fi
done

方式2

#!/bin/bash

# ==============================================================================
#  AOMSERVER 守护进程脚本
# ==============================================================================
#
# 【功能说明】
#  1. 核心守护: 崩溃自动重启、被杀自动重启、僵尸进程自动清理重启。
#  2. 自动安装: 支持通过命令一键设置开机自启。
#  3. 日志管理: 内置日志轮转,防止磁盘写满。
#  4. 单例运行: 防止重复启动。
#
# 【使用说明】
#  1. 启动服务:      ./aomserver.sh
#  2. 设置开机自启:  ./aomserver.sh --install   <-- 新增功能
#  3. 取消开机自启:  ./aomserver.sh --uninstall <-- 新增功能
#  4. 停止服务:      pkill -f aomserver.sh
#  5. 查看状态:      ./aomserver.sh --status
#  6. 查看日志:      tail -f /tmp/aomserver.log
#
# ==============================================================================

# ------------------------------------------------------------------------------
#  >>> [配置区域] 请根据您的实际情况修改以下 3 项 <<<
# ------------------------------------------------------------------------------

# [1] 你的真实启动命令 (必须是绝对路径)
# 注意: Crontab 启动时环境变量可能为空,请务必使用绝对路径 (如 /usr/bin/python3)
REAL_COMMAND="/usr/bin/python3 /opt/aom/main.py"

# [2] 进程唯一关键字
# 脚本通过 "ps -ef | grep 关键字" 判断服务是否存活
# 请确保足够独特,例如脚本名 "main.py" 或 jar包名 "app.jar"
PROCESS_KEY="main.py"

# [3] 日志文件路径
# 建议: root用户用 /var/log/aomserver.log,普通用户用 /tmp/aomserver.log
LOG_FILE="/tmp/aomserver.log"

# ------------------------------------------------------------------------------
#  >>> 系统内部变量 <<<
# ------------------------------------------------------------------------------
DAEMON_NAME="aomserver.sh"                  # 脚本名称
CHECK_INTERVAL=3                            # 检测周期(秒)
MAX_LOG_SIZE=10485760                       # 日志最大限制 10MB
PID_FILE="/tmp/aomserver.service.pid"       # 业务PID
DAEMON_PID_FILE="/tmp/aomserver.daemon.pid" # 守护PID

# 获取当前脚本的绝对路径 (用于开机自启配置)
CURRENT_SCRIPT_PATH=$(readlink -f "$0")

# ==============================================================================
#  核心工具函数
# ==============================================================================

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}

# 帮助菜单
show_help() {
    echo "用法: $0 [选项]"
    echo ""
    echo "选项:"
    echo "  (无参数)       启动守护进程"
    echo "  --install      安装开机自启 (添加到 Crontab)"
    echo "  --uninstall    移除开机自启"
    echo "  --status       查看运行状态"
    echo "  --help         显示帮助"
    echo ""
    echo "日志位置: $LOG_FILE"
}

# --- 新增: 自动安装开机自启 ---
install_autostart() {
    echo "正在设置开机自启..."

    # 1. 检查是否已经安装过
    if crontab -l 2>/dev/null | grep -Fq "$CURRENT_SCRIPT_PATH"; then
        echo "⚠️  警告: 开机自启已存在,无需重复安装。"
        exit 0
    fi

    # 2. 写入 Crontab (@reboot 任务)
    # 使用临时文件避免破坏现有的 cron 任务
    (crontab -l 2>/dev/null; echo "@reboot $CURRENT_SCRIPT_PATH") | crontab -

    if [ $? -eq 0 ]; then
        echo "✅ 成功! 已将脚本路径加入 Crontab: $CURRENT_SCRIPT_PATH"
        echo "   下次重启服务器时,此服务将自动启动。"
    else
        echo "❌ 失败! 无法修改 Crontab,请检查当前用户权限。"
    fi
}

# --- 新增: 移除开机自启 ---
uninstall_autostart() {
    echo "正在移除开机自启..."

    # 1. 过滤掉包含当前脚本路径的行,重新写入 crontab
    crontab -l 2>/dev/null | grep -Fv "$CURRENT_SCRIPT_PATH" | crontab -

    echo "✅ 已移除相关启动项。"
}

# 日志轮转 (Copy-Truncate)
rotate_log_if_needed() {
    if [ -f "$LOG_FILE" ] && [ "$(stat -c%s "$LOG_FILE")" -gt $MAX_LOG_SIZE ]; then
        log "🔄 日志文件过大,执行轮转..."
        cp "$LOG_FILE" "$LOG_FILE.$(date +%F_%H%M%S)"
        echo "" > "$LOG_FILE"
    fi
}

# 精准存活检测
is_service_running() {
    # 策略A: 检查 PID 文件
    if [ -f "$PID_FILE" ]; then
        PID=$(cat "$PID_FILE")
        if ps -p "$PID" > /dev/null 2>&1; then
            STATE=$(ps -p "$PID" -o stat= | cut -c 1)
            # 排除 Z (Zombie) 和 T (Stopped)
            if [ "$STATE" != "Z" ] && [ "$STATE" != "T" ]; then
                # 二次校验: 命令行是否包含关键字
                if ps -p "$PID" -o args= | grep -q "$PROCESS_KEY"; then
                    return 0
                fi
            fi
        fi
    fi

    # 策略B: 进程表扫描 (排除 grep, 排除守护进程自身, 排除僵尸进程)
    COUNT=$(ps -ef | grep "$PROCESS_KEY" | grep -v "grep" | grep -v "$DAEMON_NAME" | grep -v "defunct" | wc -l)
    if [ "$COUNT" -gt 0 ]; then
        # 补写 PID 文件
        pgrep -f "$PROCESS_KEY" | head -n 1 > "$PID_FILE"
        return 0
    fi

    return 1
}

start_service() {
    log "🚀 启动服务: $REAL_COMMAND"
    nohup $REAL_COMMAND >> "$LOG_FILE" 2>&1 &
    echo $! > "$PID_FILE"
    sleep 1
}

stop_service_gracefully() {
    if [ -f "$PID_FILE" ]; then
        PID=$(cat "$PID_FILE")
        if kill -0 "$PID" 2>/dev/null; then
            log "🛑 正在停止业务进程 (PID=$PID)..."
            kill "$PID"
            for i in {1..5}; do kill -0 "$PID" 2>/dev/null || break; sleep 1; done
            kill -9 "$PID" 2>/dev/null
        fi
        rm -f "$PID_FILE"
    else
        pkill -f "$PROCESS_KEY"
    fi
}

# ==============================================================================
#  主逻辑控制
# ==============================================================================

# 确保脚本可执行
chmod +x "$0"

case "$1" in
    --help)
        show_help
        exit 0
        ;;
    --install)
        install_autostart
        exit 0
        ;;
    --uninstall)
        uninstall_autostart
        exit 0
        ;;
    --status)
        if pgrep -f "$DAEMON_NAME" | grep -v $$ > /dev/null; then
            echo "✅ 守护进程正在运行."
            echo "   守护进程 PID: $(cat $DAEMON_PID_FILE 2>/dev/null)"
            echo "   服务运行状态: $(is_service_running && echo '运行中' || echo '未运行')"
        else
            echo "❌ 守护进程未运行."
        fi
        exit 0
        ;;
    --daemon-mode)
        # 内部模式,请勿手动调用
        ;;
    *)
        # 默认启动检查
        if [ -f "$DAEMON_PID_FILE" ]; then
            OLD_PID=$(cat "$DAEMON_PID_FILE")
            if ps -p "$OLD_PID" > /dev/null 2>&1 && ps -p "$OLD_PID" -o args= | grep -q "$DAEMON_NAME"; then
                echo "❌ 错误: AOMServer 守护进程已在运行 (PID=$OLD_PID)"
                echo "💡 提示: 停止命令: pkill -f $DAEMON_NAME"
                exit 1
            fi
        fi

        echo "✅ 正在启动守护进程..."
        nohup "$0" --daemon-mode >> "$LOG_FILE" 2>&1 &
        echo "✅ 启动成功! 日志文件: $LOG_FILE"
        exit 0
        ;;
esac

# ==============================================================================
#  守护进程后台循环 (仅后台模式运行)
# ==============================================================================

echo $$ > "$DAEMON_PID_FILE"
log ">>> 守护进程初始化 (Monitor PID: $$)"

# 信号捕获 (清理逻辑)
trap 'log "📴 收到停止信号,退出。"; stop_service_gracefully; rm -f "$DAEMON_PID_FILE"; exit 0' SIGTERM SIGINT

while true; do
    rotate_log_if_needed

    if ! is_service_running; then
        log "⚠️  检测到服务异常,准备重启..."
        start_service
        log "✅ 服务已重新拉起"
    fi

    sleep "$CHECK_INTERVAL"
done

运行监测脚本指令,返回的信息大于0说明运行成功了。注意:先开启服务,再开启监测脚本。

nohup bash MonitoringScripts.sh &       #运行

至此整个监测进程就搭建完成了。

补充:本文可以搭配这个场景使用。https://blog.hbb.cloudns.org/article/000011/.html