Python实战----拒绝“裸奔”!手把手教你写一个高可用的网站监控告警脚本
摘要:本文介绍如何使用Python开发一个实用的网站健康监控脚本。该脚本能定时检查多个目标网站的状态码和响应时间,在服务异常时自动发送邮件告警,并详细记录运行日志。核心功能包括:多URL监控、HTTP请求超时设置、完善的异常处理、邮件通知机制以及规范的日志记录。文章详细解析了脚本设计思路,提供了完整代码实现,并分享了生产环境部署建议。这个轻量级解决方案特别适合中小项目或个人开发者,帮助实现服务的自
摘要:你是否遇到过网站突然宕机,自己却是在用户投诉后才知道的尴尬境地?作为开发者或运维,手动F5刷新页面来监控服务显然不现实。本文将带你使用Python编写一个高可用、实用级的网站健康监控脚本。它不仅能定时检查目标网站的状态码和响应时间,还能在服务异常时第一时间发送邮件告警,并详细记录运行日志。麻雀虽小,五脏俱全,掌握它,让你的服务监控不再“裸奔”。
🚀 引言:为什么需要这个脚本?
在数字化时代,服务的可用性至关重要。无论是个人博客、公司官网还是关键的API服务,一旦出现故障(如 500 错误、连接超时等),我们需要在第一时间获知,而不是等待用户反馈。
虽然市面上有著名的监控服务(如 Uptime Robot, Datadog 等),但对于很多中小型项目或个人开发者来说,它们要么收费,要么配置繁琐。
Python 作为胶水语言,极其适合完成这种自动化运维任务。今天我们将构建的脚本,目标是实现以下核心功能:
-
多目标监控:支持同时监控多个 URL。
-
深度检查:不仅检查 HTTP 状态码是否为 200,还监测响应时间是否超时。
-
健壮性设计:加入完善的异常捕获( Try/Except)和超时设置,防止脚本自身因网络波动而挂掉。
-
实时告警:服务异常时,通过 SMTP 发送邮件通知。
-
规范日志:使用标准
logging模块记录一切,便于事后复盘。
🛠️ 准备工作
本项目依赖 Python 标准库以及一个第三方库 requests(用于更优雅地处理 HTTP 请求)。
-
环境要求:Python 3.6+
-
安装依赖:
pip install requests -
准备一个发送邮件的邮箱:建议使用 163邮箱、QQ邮箱或 Gmail。注意: 为了安全起见,现代邮箱通常需要开启 SMTP 服务并生成授权码(App Password),而不是直接使用登录密码。请提前去邮箱设置中获取。
💡 核心思路解析
一个高可用的监控脚本,其核心流程是一个无限循环的定时任务:
graph TD
A[启动脚本] --> B[读取配置(URL列表, 邮箱信息)];
B --> C{进入主循环};
C --> D[遍历监控目标 URL];
D --> E[发起 HTTP GET 请求 (设置超时)];
E --> F{请求成功且状态码为200?};
F -- Yes --> G[记录正常日志];
F -- No --> H[记录错误日志 & 发送告警邮件];
G --> I[检查下一个 URL];
H --> I;
I --> J{所有 URL 检查完毕?};
J -- Yes --> K[休眠 N 秒 (sleep)];
J -- No --> D;
K --> C;
💻 完整代码实战
以下是完整的脚本代码。你可以将其保存为 monitor.py。代码中已包含详细注释。
import requests
import smtplib
import time
import logging
from email.mime.text import MIMEText
from email.utils import formatdate
from requests.exceptions import RequestException, Timeout, ConnectionError
# ================= 配置区域 (请修改这里) =================
# 1. 监控目标列表
TARGET_URLS = [
"https://www.baidu.com",
"https://github.com",
# 在这里添加你自己的网站,例如 "http://your-website.com/api/health"
# "https://httpbin.org/status/500", # 测试用:故意模拟错误的地址
]
# 2. 监控配置
CHECK_INTERVAL = 60 * 5 # 检查间隔(秒),建议 300 (5分钟)
REQUEST_TIMEOUT = 10 # 请求超时时间(秒),超过此时间视为目标不可用
# 3. 邮件告警配置 (以 163 邮箱为例,其他邮箱请查找对应的 SMTP 设置)
SMTP_SERVER = "smtp.163.com" # SMTP 服务器地址
SMTP_PORT = 465 # SMTP SSL 端口 (通常是 465)
SENDER_EMAIL = "your_email@163.com" # 发件人邮箱
# 【重要】这里填写的是邮箱授权码,不是登录密码!
SENDER_PASSWORD = "YOUR_AUTH_CODE_HERE"
RECEIVER_EMAILS = ["receiver1@example.com"] # 收件人列表
# ================= 日志配置 =================
# 配置日志输出格式,同时输出到控制台和文件
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - [%(levelname)s] - %(message)s',
handlers=[
logging.FileHandler("monitor.log", encoding='utf-8'),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
# ================= 核心功能函数 =================
def send_alert_email(subject, message_body):
"""
发送告警邮件的核心函数
"""
try:
msg = MIMEText(message_body, 'plain', 'utf-8')
msg['Subject'] = f"【网站监控告警】{subject}"
msg['From'] = SENDER_EMAIL
msg['To'] = ",".join(RECEIVER_EMAILS)
msg['Date'] = formatdate(localtime=True)
# 使用 SSL 加密连接 SMTP 服务器
with smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT, timeout=15) as server:
server.login(SENDER_EMAIL, SENDER_PASSWORD)
server.sendmail(SENDER_EMAIL, RECEIVER_EMAILS, msg.as_string())
logger.info(f"告警邮件已成功发送给: {RECEIVER_EMAILS}")
return True
except smtplib.SMTPAuthenticationError:
logger.critical("邮件发送失败:认证错误。请检查邮箱账号和授权码是否正确。")
except Exception as e:
logger.error(f"邮件发送失败,发生未知错误: {e}")
return False
def check_site_health(url):
"""
检查单个站点的健康状况
返回: (是否健康 bool, 状态描述 str)
"""
try:
start_time = time.time()
# 发起请求,务必设置 timeout,防止脚本卡死
response = requests.get(url, timeout=REQUEST_TIMEOUT)
end_time = time.time()
response_time = round((end_time - start_time) * 1000, 2) # 毫秒
if response.status_code == 200:
logger.info(f"[正常] {url} - 状态码: {response.status_code} - 耗时: {response_time}ms")
return True, f"OK (Time: {response_time}ms)"
else:
error_msg = f"异常状态码: {response.status_code}"
logger.warning(f"[异常] {url} - {error_msg}")
return False, error_msg
except Timeout:
error_msg = f"请求超时 (>{REQUEST_TIMEOUT}s)"
logger.error(f"[不可用] {url} - {error_msg}")
return False, error_msg
except ConnectionError:
error_msg = "连接失败 (无法解析域名或连接被拒绝)"
logger.error(f"[不可用] {url} - {error_msg}")
return False, error_msg
except RequestException as e:
# 捕获其他所有 requests 相关的异常
error_msg = f"发生请求错误: {str(e)}"
logger.error(f"[错误] {url} - {error_msg}")
return False, error_msg
# ================= 主循环 =================
def run_monitor_loop():
"""
主程序入口
"""
logger.info("=== 网站监控脚本已启动 ===")
logger.info(f"监控目标数: {len(TARGET_URLS)}, 检查间隔: {CHECK_INTERVAL}秒")
# 用于记录上一次的告警状态,避免重复发送告警(简单的防抖动)
# 格式: {url: is_down_last_time_bool}
alert_history = {url: False for url in TARGET_URLS}
while True:
logger.debug("开始新一轮检查...")
for url in TARGET_URLS:
is_healthy, msg = check_site_health(url)
if not is_healthy:
# 如果当前不健康,且上一次是健康的(或者刚启动),则发送告警
if not alert_history[url]:
alert_subject = f"目标无法访问: {url}"
alert_body = (
f"检测时间: {time.strftime('%Y-%m-%d %H:%M:%S')}\n"
f"故障目标: {url}\n"
f"错误信息: {msg}\n\n"
f"请相关人员尽快排查。"
)
# 发送邮件
send_alert_email(alert_subject, alert_body)
# 更新状态,标记已告警
alert_history[url] = True
else:
# 如果当前健康,且上一次是不健康的,可以发送一个恢复通知(可选)
if alert_history[url]:
logger.info(f"[{url}] 服务已恢复正常。")
# 发送恢复邮件(可根据需要取消注释)
# send_alert_email(f"服务恢复: {url}", f"{url} 现已恢复正常访问。")
# 更新状态,标记为正常
alert_history[url] = False
logger.debug(f"本轮检查结束,休眠 {CHECK_INTERVAL} 秒...")
time.sleep(CHECK_INTERVAL)
if __name__ == "__main__":
# 建议在服务器使用 nohup python -u monitor.py > /dev/null 2>&1 & 后台运行
run_monitor_loop()
🌟 代码亮点与高可用设计解析
这段代码虽然不长,但包含了很多生产环境脚本的必备要素:
1. 完善的日志记录 (Logging)
我们没有使用简陋的 print()。
logging.basicConfig(..., handlers=[logging.FileHandler("monitor.log"), logging.StreamHandler()])
通过配置 logging 模块,脚本会将运行信息同时输出到控制台(方便调试)和 monitor.log 文件(方便持久化保存和排查历史故障)。每条日志都带有精确的时间戳和日志级别(INFO, WARNING, ERROR)。
2. 健壮的网络请求与超时控制 (Timeout)
这是监控脚本最关键的部分!
requests.get(url, timeout=REQUEST_TIMEOUT)
如果不设置 timeout,一旦目标网站卡死(既不返回数据也不断开连接),你的监控脚本也会随之无限期挂起,失去监控能力。设置超时是高可用脚本的基本素养。
3. 精细化的异常处理 (Try/Except)
我们没有简单地使用一个裸露的 except Exception。
except Timeout: ...
except ConnectionError: ...
except RequestException as e: ...
代码专门捕获了 requests 库可能抛出的特定异常,如超时、连接错误等。这样我们就能区分网站是“慢”、是“挂了”还是“域名解析不了”,并在日志和邮件中提供更准确的错误信息。同时,邮件发送模块也单独进行了异常处理,防止因为邮件发不出去而导致整个监控循环崩溃。
4. 简单的告警防抖动机制 (Debouncing)
试想一下,如果一个网站挂了 1 个小时,每 5 分钟检查一次,你不想收到 12 封重复的“网站挂了”的邮件吧?
alert_history = {url: False for url in TARGET_URLS}
# ...
if not is_healthy:
if not alert_history[url]: # 只有上一次状态是正常时,才发送告警
send_alert_email(...)
alert_history[url] = True
通过 alert_history 字典记录每个 URL 上一次的状态。只有当状态从“正常”变为“异常”的那个瞬间,才触发告警。这大大减少了垃圾邮件的轰炸。
🚀 如何运行与扩展
运行指南
-
修改代码中的配置区域,填入你的监控 URL 和邮箱 SMTP 信息(切记使用授权码)。
-
在终端运行:
python monitor.py -
服务器部署建议:在 Linux 服务器上,为了让脚本在后台稳定运行,并在退出终端后不中断,可以使用
nohup命令:# -u 表示禁用输出缓存,让日志实时写入文件 nohup python -u monitor.py > /dev/null 2>&1 &运行后,可以通过
tail -f monitor.log查看实时运行日志。
扩展思路
这个脚本是一个坚实的基础,你可以继续扩展它:
-
增加通知渠道:除了邮件,可以接入钉钉机器人、企业微信 Webhook 或 Telegram Bot 发送告警,即时性更强。
-
内容校验:目前的脚本只检查状态码。你可以增加逻辑,检查返回的 HTML 页面中是否包含特定的关键词(例如检查页面是否有 "Welcome"),以防止页面虽然返回 200 但内容却是空白的情况。
-
使用专业调度库:对于更复杂的定时任务,可以使用
APScheduler库代替简单的while True + sleep循环。 -
配置文件分离:将敏感的邮箱配置信息从代码中剥离,存放到环境变量或单独的
.env/config.ini文件中,提高安全性。
📝 总结
本文介绍了一个 Python 实用脚本的完整开发过程。我们不仅实现了基础的网站监控功能,更重要的是融入了日志规范、异常处理、超时控制、告警防抖等“高可用”的设计思维。
希望这个实战案例能让你体会到 Python 在自动化运维领域的魅力。别再让你的服务“裸奔”了,赶紧动手部署一个属于你自己的监控探针吧!
如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、关注一波!你的支持是我持续创作的动力! 👍
更多推荐



所有评论(0)