本文目录导读:

我来为您提供一个完整的PHP自动化巡检脚本方案,包含服务器健康检查、安全检查和性能监控等功能。
基础巡检脚本
server_check.php
<?php
/**
* PHP服务器自动化巡检脚本
* 用于监控服务器健康状态、性能指标和安全状况
*/
class ServerHealthCheck {
private $results = [];
private $thresholds = [
'cpu_load' => 0.8, // CPU使用率阈值 80%
'memory_limit' => 0.9, // 内存使用率阈值 90%
'disk_limit' => 0.85, // 磁盘使用率阈值 85%
'response_time' => 2.0, // 响应时间阈值(秒)
];
public function __construct($thresholds = []) {
if (!empty($thresholds)) {
$this->thresholds = array_merge($this->thresholds, $thresholds);
}
}
/**
* 运行所有检查项目
*/
public function runAllChecks() {
$this->checkSystemInfo();
$this->checkCPUUsage();
$this->checkMemoryUsage();
$this->checkDiskUsage();
$this->checkNetworkConnection();
$this->checkWebServerStatus();
$this->checkPhpExtensions();
$this->checkFilePermissions();
$this->checkSecurity();
$this->checkErrorLogs();
return $this->generateReport();
}
/**
* 检查系统基本信息
*/
private function checkSystemInfo() {
$this->results['system'] = [
'os' => php_uname(),
'php_version' => PHP_VERSION,
'server_software' => $_SERVER['SERVER_SOFTWARE'] ?? 'Unknown',
'server_ip' => $_SERVER['SERVER_ADDR'] ?? gethostbyname(gethostname()),
'uptime' => $this->getSystemUptime(),
'date' => date('Y-m-d H:i:s')
];
}
/**
* 检查CPU使用率
*/
private function checkCPUUsage() {
$cpuInfo = $this->getCPUUsage();
$status = $cpuInfo['usage'] < ($this->thresholds['cpu_load'] * 100) ? 'OK' : 'WARNING';
$this->results['cpu'] = [
'status' => $status,
'usage_percent' => round($cpuInfo['usage'], 2),
'cores' => $cpuInfo['cores'],
'details' => $cpuInfo['details']
];
}
/**
* 获取CPU使用率
*/
private function getCPUUsage() {
// Linux系统实现
if (PHP_OS_FAMILY === 'Linux') {
$load = sys_getloadavg();
$cores = $this->getCpuCoreCount();
$usage = $load[0] / $cores * 100;
return [
'usage' => $usage,
'cores' => $cores,
'details' => "Load average: " . implode(', ', array_map(fn($v) => round($v, 2), $load))
];
}
// Windows系统实现(简化版)
$info = exec('wmic cpu get loadpercentage /value');
preg_match('/LoadPercentage=(\d+)/', $info, $matches);
$usage = isset($matches[1]) ? (float)$matches[1] : 0;
return [
'usage' => $usage,
'cores' => 1,
'details' => 'Windows CPU info'
];
}
/**
* 获取CPU核心数
*/
private function getCpuCoreCount() {
if (PHP_OS_FAMILY === 'Linux') {
return (int)shell_exec('nproc') ?: 1;
}
return 1;
}
/**
* 检查内存使用率
*/
private function checkMemoryUsage() {
$memoryInfo = $this->getMemoryUsage();
$usagePercent = $memoryInfo['used'] / $memoryInfo['total'] * 100;
$status = $usagePercent < ($this->thresholds['memory_limit'] * 100) ? 'OK' : 'WARNING';
$this->results['memory'] = [
'status' => $status,
'total' => $this->formatBytes($memoryInfo['total']),
'used' => $this->formatBytes($memoryInfo['used']),
'free' => $this->formatBytes($memoryInfo['free']),
'usage_percent' => round($usagePercent, 2),
'details' => $memoryInfo['raw']
];
}
/**
* 获取内存使用信息
*/
private function getMemoryUsage() {
if (PHP_OS_FAMILY === 'Linux') {
$meminfo = file_get_contents('/proc/meminfo');
preg_match('/MemTotal:\s+(\d+)/', $meminfo, $total);
preg_match('/MemAvailable:\s+(\d+)/', $meminfo, $available);
$totalBytes = $total[1] * 1024; // KB to bytes
$availableBytes = $available[1] * 1024;
return [
'total' => $totalBytes,
'used' => $totalBytes - $availableBytes,
'free' => $availableBytes,
'raw' => $meminfo
];
}
// Windows系统
$output = [];
exec('wmic OS get TotalVisibleMemorySize,FreePhysicalMemory /Value', $output);
$totalKb = 0;
$freeKb = 0;
foreach ($output as $line) {
if (strpos($line, 'TotalVisibleMemorySize=') !== false) {
$totalKb = (int)str_replace('TotalVisibleMemorySize=', '', $line);
}
if (strpos($line, 'FreePhysicalMemory=') !== false) {
$freeKb = (int)str_replace('FreePhysicalMemory=', '', $line);
}
}
return [
'total' => $totalKb * 1024,
'used' => ($totalKb - $freeKb) * 1024,
'free' => $freeKb * 1024,
'raw' => 'Windows memory info'
];
}
/**
* 检查磁盘使用率
*/
private function checkDiskUsage() {
$disks = [];
$issues = [];
if (PHP_OS_FAMILY === 'Linux') {
$df = shell_exec('df -h');
$lines = explode("\n", $df);
array_shift($lines); // 移除标题行
foreach ($lines as $line) {
if (trim($line) === '') continue;
$parts = preg_split('/\s+/', $line);
if (count($parts) >= 5) {
$disk = [
'filesystem' => $parts[0],
'size' => $parts[1],
'used' => $parts[2],
'available' => $parts[3],
'usage_percent' => (int)str_replace('%', '', $parts[4]),
'mounted_on' => $parts[5] ?? ''
];
$disks[] = $disk;
if ($disk['usage_percent'] > ($this->thresholds['disk_limit'] * 100)) {
$issues[] = "磁盘 {$disk['mounted_on']} 使用率超过阈值: {$disk['usage_percent']}%";
}
}
}
} else {
// Windows等其他系统检查
$output = [];
exec('wmic logicaldisk get deviceid,size,freespace /Value', $output);
// 解析输出...
}
$this->results['disk'] = [
'status' => empty($issues) ? 'OK' : 'WARNING',
'disks' => $disks,
'issues' => $issues
];
}
/**
* 检查网络连接
*/
private function checkNetworkConnection() {
$connections = [];
$issues = [];
// 检查外部连接
$externalHosts = [
'google' => 'google.com',
'github' => 'github.com'
];
foreach ($externalHosts as $name => $host) {
$pingTime = $this->pingHost($host);
$connections[$name] = $pingTime;
if ($pingTime === false) {
$issues[] = "无法连接到 $host";
}
}
// 检查数据库连接(如果配置了)
if (defined('DB_HOST')) {
$dbConnection = $this->checkDatabaseConnection();
$connections['database'] = $dbConnection;
if (!$dbConnection['success']) {
$issues[] = "数据库连接失败";
}
}
$this->results['network'] = [
'status' => empty($issues) ? 'OK' : 'WARNING',
'connections' => $connections,
'issues' => $issues
];
}
/**
* Ping外部主机
*/
private function pingHost($host) {
$startTime = microtime(true);
if (PHP_OS_FAMILY === 'Linux') {
$output = [];
exec("ping -c 1 -w 2 $host", $output, $returnCode);
if ($returnCode === 0) {
$time = microtime(true) - $startTime;
return round($time * 1000); // 毫秒
}
return false;
} else {
// Windows
$output = [];
exec("ping -n 1 -w 2000 $host", $output, $returnCode);
if ($returnCode === 0) {
$time = microtime(true) - $startTime;
return round($time * 1000);
}
return false;
}
}
/**
* 检查数据库连接
*/
private function checkDatabaseConnection() {
try {
$pdo = new PDO(
'mysql:host=' . DB_HOST . ';dbname=' . DB_NAME,
DB_USER,
DB_PASS,
[PDO::ATTR_TIMEOUT => 2]
);
return [
'success' => true,
'info' => 'Database connection OK'
];
} catch (Exception $e) {
return [
'success' => false,
'info' => 'Database connection failed: ' . $e->getMessage()
];
}
}
/**
* 检查Web服务器状态
*/
private function checkWebServerStatus() {
$issues = [];
$serverInfo = [];
// 检查Apache/Nginx进程
if (PHP_OS_FAMILY === 'Linux') {
foreach (['apache2', 'nginx', 'httpd'] as $service) {
$output = [];
exec("ps aux | grep -v grep | grep $service", $output);
if (!empty($output)) {
$serverInfo[$service] = 'Running';
} else {
$serverInfo[$service] = 'Not running';
$issues[] = "$service is not running";
}
}
}
// 检查PHP-FPM
if (PHP_OS_FAMILY === 'Linux') {
$output = [];
exec("ps aux | grep -v grep | grep php-fpm", $output);
$serverInfo['php-fpm'] = !empty($output) ? 'Running' : 'Not running';
if (empty($output)) {
$issues[] = 'PHP-FPM is not running';
}
}
$this->results['web_server'] = [
'status' => empty($issues) ? 'OK' : 'WARNING',
'services' => $serverInfo,
'issues' => $issues
];
}
/**
* 检查PHP扩展
*/
private function checkPhpExtensions() {
$requiredExtensions = [
'mysqli', 'pdo_mysql', 'curl', 'json', 'gd', 'mbstring',
'xml', 'zip', 'openssl', 'redis', 'memcached'
];
$extensions = [];
$missingExtensions = [];
foreach ($requiredExtensions as $ext) {
$extensions[$ext] = extension_loaded($ext) ? 'Enabled' : 'Disabled';
if (!extension_loaded($ext)) {
$missingExtensions[] = $ext;
}
}
$this->results['php_extensions'] = [
'status' => empty($missingExtensions) ? 'OK' : 'WARNING',
'extensions' => $extensions,
'missing' => $missingExtensions
];
}
/**
* 检查文件权限
*/
private function checkFilePermissions() {
$criticalPaths = [
'web_root' => $_SERVER['DOCUMENT_ROOT'] ?? '.',
'upload_dir' => 'uploads',
'log_dir' => 'logs',
'config' => 'config.php'
];
$issues = [];
$permissions = [];
foreach ($criticalPaths as $name => $path) {
if (file_exists($path)) {
$perms = fileperms($path);
$writable = is_writable($path);
$permissions[$name] = [
'path' => $path,
'permissions' => substr(sprintf('%o', $perms), -4),
'writable' => $writable
];
if (!$writable) {
$issues[] = "目录/文件 $path 不可写";
}
}
}
// 检查敏感文件是否可访问
$sensitiveFiles = ['.env', 'config.php', 'database.sql'];
foreach ($sensitiveFiles as $file) {
if (file_exists($file) && !$this->isFileProtected($file)) {
$issues[] = "敏感文件 $file 可能暴露在Web环境下";
}
}
$this->results['permissions'] = [
'status' => empty($issues) ? 'OK' : 'WARNING',
'permissions' => $permissions,
'issues' => $issues
];
}
/**
* 检查文件是否受保护
*/
private function isFileProtected($file) {
// 检查是否在web根目录之外
$absolutePath = realpath($file);
$webRoot = realpath($_SERVER['DOCUMENT_ROOT'] ?? '.');
return strpos($absolutePath, $webRoot) !== 0;
}
/**
* 安全检查
*/
private function checkSecurity() {
$securityIssues = [];
$checks = [];
// 检查PHP版本安全性
$phpVersion = PHP_VERSION;
$checks['php_version'] = "PHP版本: $phpVersion";
if (version_compare($phpVersion, '7.4', '<')) {
$securityIssues[] = "PHP版本过低,建议升级到7.4以上";
}
// 检查显示错误设置
$displayErrors = ini_get('display_errors');
$checks['display_errors'] = $displayErrors ? 'Enabled' : 'Disabled';
if ($displayErrors) {
$securityIssues[] = "display_errors已开启,建议在生产环境关闭";
}
// 检查错误报告级别
$errorReporting = error_reporting();
$checks['error_reporting'] = $errorReporting;
// 检查文件上传设置
$fileUploads = ini_get('file_uploads');
$checks['file_uploads'] = $fileUploads ? 'Enabled' : 'Disabled';
// 检查open_basedir
$openBasedir = ini_get('open_basedir');
$checks['open_basedir'] = $openBasedir ? $openBasedir : 'Not Set';
if (empty($openBasedir)) {
$securityIssues[] = "open_basedir未设置,可能存在目录遍历风险";
}
// 检查关键函数是否被禁用
$dangerousFunctions = ['exec', 'system', 'passthru', 'shell_exec', 'proc_open'];
$disabledFunctions = array_filter(explode(',', ini_get('disable_functions')));
foreach ($dangerousFunctions as $func) {
if (!in_array(trim($func), $disabledFunctions)) {
$checks["func_$func"] = "未禁用 $func()";
$securityIssues[] = "高风险函数 $func() 未被禁用";
}
}
// 检查HTTPS
$https = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
$checks['https'] = $https ? 'Enabled' : 'Disabled';
if (!$https && PHP_SAPI !== 'cli') {
$securityIssues[] = "当前未使用HTTPS连接";
}
$this->results['security'] = [
'status' => empty($securityIssues) ? 'OK' : 'WARNING',
'checks' => $checks,
'issues' => $securityIssues
];
}
/**
* 检查错误日志
*/
private function checkErrorLogs() {
$logFile = ini_get('error_log');
$issues = [];
$recentErrors = [];
if (file_exists($logFile)) {
$logSize = filesize($logFile);
$mtime = filemtime($logFile);
// 读取最后100行日志
$lines = $this->getLastLines($logFile, 100);
foreach ($lines as $line) {
if (preg_match('/\[error\]|\[warning\]|Fatal error/i', $line)) {
$recentErrors[] = trim($line);
if (strpos($line, 'Fatal error') !== false) {
$issues[] = "发现致命错误: $line";
}
}
}
$this->results['error_log'] = [
'status' => empty($issues) ? 'OK' : 'WARNING',
'log_file' => $logFile,
'log_size' => $this->formatBytes($logSize),
'last_modified' => date('Y-m-d H:i:s', $mtime),
'recent_errors' => array_slice($recentErrors, -5),
'issues' => $issues
];
} else {
$this->results['error_log'] = [
'status' => 'WARNING',
'issues' => ['未找到错误日志文件: ' . $logFile]
];
}
}
/**
* 读取文件最后几行
*/
private function getLastLines($file, $lines = 100) {
if (PHP_OS_FAMILY === 'Linux') {
return explode("\n", shell_exec("tail -n $lines $file"));
}
// 其他系统使用PHP实现
$content = file_get_contents($file);
$contentLines = explode("\n", $content);
return array_slice($contentLines, -$lines);
}
/**
* 生成报告
*/
private function generateReport() {
$summary = [
'total_checks' => 0,
'ok' => 0,
'warnings' => 0,
'errors' => 0
];
foreach ($this->results as $category => $result) {
if (isset($result['status'])) {
$summary['total_checks']++;
switch ($result['status']) {
case 'OK':
$summary['ok']++;
break;
case 'WARNING':
$summary['warnings']++;
break;
case 'ERROR':
$summary['errors']++;
break;
}
}
}
// 设置整体状态
$overallStatus = 'OK';
if ($summary['errors'] > 0) {
$overallStatus = 'ERROR';
} elseif ($summary['warnings'] > 0) {
$overallStatus = 'WARNING';
}
return [
'overall_status' => $overallStatus,
'summary' => $summary,
'timestamp' => date('Y-m-d H:i:s'),
'details' => $this->results
];
}
/**
* 格式化字节
*/
private function formatBytes($bytes, $precision = 2) {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= pow(1024, $pow);
return round($bytes, $precision) . ' ' . $units[$pow];
}
/**
* 获取系统运行时间
*/
private function getSystemUptime() {
if (PHP_OS_FAMILY === 'Linux') {
$uptime = shell_exec('cat /proc/uptime');
$seconds = floatval(explode(' ', $uptime)[0]);
$days = floor($seconds / 86400);
$hours = floor(($seconds % 86400) / 3600);
$minutes = floor(($seconds % 3600) / 60);
return "$days天 $hours小时 $minutes分钟";
}
return 'System uptime info unavailable';
}
}
// 使用方法示例
if (PHP_SAPI === 'cli') {
// 命令行模式
$healthCheck = new ServerHealthCheck();
$report = $healthCheck->runAllChecks();
echo json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . PHP_EOL;
}
数据库巡检脚本
database_check.php
<?php
/**
* 数据库巡检脚本
*/
function checkDatabaseHealth() {
// 数据库配置
$dbConfig = [
'host' => 'localhost',
'name' => 'your_database',
'user' => 'your_user',
'pass' => 'your_password',
'port' => 3306
];
$results = [
'status' => 'OK',
'checks' => []
];
try {
$pdo = new PDO(
"mysql:host={$dbConfig['host']};dbname={$dbConfig['name']};charset=utf8mb4",
$dbConfig['user'],
$dbConfig['pass'],
[
PDO::ATTR_TIMEOUT => 5,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]
);
// 1. 数据库版本
$version = $pdo->query('SELECT VERSION()')->fetchColumn();
$results['checks']['version'] = $version;
// 2. 数据库状态
$status = $pdo->query('SHOW STATUS')->fetchAll(PDO::FETCH_KEY_PAIR);
$results['checks']['qps'] = $status['Questions'] ?? 'N/A'; // 查询次数
// 3. 连接数
$connections = $pdo->query('SHOW STATUS LIKE "Threads_connected"')->fetch();
$maxConnections = $pdo->query('SHOW VARIABLES LIKE "max_connections"')->fetch();
$currentConn = (int)$connections['Value'];
$maxConn = (int)$maxConnections['Value'];
$usagePercent = ($currentConn / $maxConn) * 100;
$results['checks']['connections'] = [
'current' => $currentConn,
'max' => $maxConn,
'usage_percent' => round($usagePercent, 2)
];
if ($usagePercent > 85) {
$results['status'] = 'WARNING';
$results['issues'][] = "数据库连接数使用率过高: {$usagePercent}%";
}
// 4. 慢查询
$slowQueries = $pdo->query('SHOW STATUS LIKE "Slow_queries"')->fetch();
$results['checks']['slow_queries'] = $slowQueries['Value'] ?? 'N/A';
// 5. InnoDB状态
$innodbStatus = $pdo->query('SHOW ENGINE INNODB STATUS')->fetch();
if ($innodbStatus) {
preg_match('/Active transactions: (\d+)/', $innodbStatus['Status'], $matches);
$results['checks']['active_transactions'] = $matches[1] ?? 'N/A';
}
// 6. 数据库表检查
$tables = $pdo->query('SHOW TABLES')->fetchAll(PDO::FETCH_COLUMN);
$brokenTables = [];
foreach ($tables as $table) {
$checkResult = $pdo->query("CHECK TABLE $table")->fetch();
if ($checkResult['Msg_type'] === 'error') {
$brokenTables[] = $table;
}
}
$results['checks']['tables'] = [
'total' => count($tables),
'broken' => $brokenTables
];
// 7. 数据库大小
$dbSize = $pdo->query(
"SELECT table_schema AS 'database',
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS 'size_mb'
FROM information_schema.tables
WHERE table_schema = '{$dbConfig['name']}'
GROUP BY table_schema"
)->fetch();
$results['checks']['database_size'] = $dbSize['size_mb'] . ' MB';
// 8. 最后一次备份时间
$backupDir = '/path/to/backup/dir'; // 配置备份目录
if (is_dir($backupDir)) {
$backupFiles = glob($backupDir . '/*');
if (!empty($backupFiles)) {
$latestBackup = max(array_map('filemtime', $backupFiles));
$daysSinceBackup = (time() - $latestBackup) / 86400;
$results['checks']['last_backup'] = date('Y-m-d H:i:s', $latestBackup);
$results['checks']['backup_age_days'] = round($daysSinceBackup, 2);
if ($daysSinceBackup > 1) {
$results['status'] = 'WARNING';
$results['issues'][] = "数据库超过1天未备份";
}
}
}
} catch (PDOException $e) {
$results['status'] = 'ERROR';
$results['message'] = '数据库连接失败: ' . $e->getMessage();
}
return $results;
}
// 执行检查
$report = checkDatabaseHealth();
echo json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
配置文件和定时任务
config.php
<?php
// 数据库配置(用于巡检脚本)
define('DB_HOST', 'localhost');
define('DB_NAME', 'your_database');
define('DB_USER', 'your_user');
define('DB_PASS', 'your_password');
// 邮件通知配置
define('MAIL_ENABLED', true);
define('MAIL_TO', 'admin@example.com');
define('MAIL_FROM', 'monitor@example.com');
// Webhook通知配置
define('WEBHOOK_ENABLED', true);
define('WEBHOOK_URL', 'https://hooks.slack.com/services/YOUR/WEBHOOK/URL');
// 短信通知配置(示例)
define('SMS_ENABLED', false);
notification.php
<?php
/**
* 通知发送功能
*/
class NotificationCenter {
public static function sendReport($report) {
$messages = [];
if ($report['overall_status'] !== 'OK') {
// 发送邮件
if (defined('MAIL_ENABLED') && MAIL_ENABLED) {
self::sendEmail($report);
}
// 发送Webhook
if (defined('WEBHOOK_ENABLED') && WEBHOOK_ENABLED) {
self::sendWebhook($report);
}
}
return true;
}
private static function sendEmail($report) {
$subject = "[{$report['overall_status']}] 服务器巡检报告 - " . date('Y-m-d H:i:s');
// 生成HTML报告
$htmlReport = self::generateHtmlReport($report);
$headers = "MIME-Version: 1.0\r\n";
$headers .= "Content-type: text/html; charset=utf-8\r\n";
$headers .= "From: " . MAIL_FROM . "\r\n";
// 使用mail函数或PHPMailer
mail(MAIL_TO, $subject, $htmlReport, $headers);
}
private static function sendWebhook($report) {
$payload = [
'text' => "🚨 *服务器巡检提醒*\n" .
"*状态:* {$report['overall_status']}\n" .
"*时间:* {$report['timestamp']}\n" .
"*警告数:* {$report['summary']['warnings']}\n" .
"*错误数:* {$report['summary']['errors']}"
];
$ch = curl_init(WEBHOOK_URL);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);
curl_close($ch);
}
private static function generateHtmlReport($report) {
$html = '<h2>服务器巡检报告</h2>';
$html .= "<p>状态: <strong>{$report['overall_status']}</strong></p>";
$html .= "<p>时间: {$report['timestamp']}</p>";
foreach ($report['details'] as $category => $details) {
$html .= "<h3>{$category}</h3>";
if (isset($details['status'])) {
$html .= "<p>状态: {$details['status']}</p>";
}
if (isset($details['issues']) && !empty($details['issues'])) {
$html .= '<ul>';
foreach ($details['issues'] as $issue) {
$html .= "<li>$issue</li>";
}
$html .= '</ul>';
}
}
return $html;
}
}
// 如果报告完成,发送通知
NotificationCenter::sendReport($report);
定时任务配置
crontab 配置示例
# 每天凌晨2点执行服务器巡检 0 2 * * * php /path/to/server_check.php >> /var/log/php_check.log 2>&1 # 每30分钟检查一次Web服务状态 */30 * * * * php /path/to/web_service_check.php # 每天检查数据库状态 30 3 * * * php /path/to/database_check.php # 每周日完整巡检 0 4 * * 0 php /path/to/full_check.php
使用说明
配置文件
- 将上述代码保存为对应文件
- 修改
config.php中的数据库配置和通知配置 - 测试运行:
php server_check.php
主要功能
- 系统检查: CPU、内存、磁盘使用情况
- 性能监控: 响应时间、负载均衡
- 安全扫描: 敏感文件、危险函数、安全配置
- 数据库检查: 连接数、慢查询、表完整性
- Web服务检查: Apache/Nginx、PHP-FPM状态
通知方式
- 邮件通知
- Webhook(如Slack、钉钉)
- 可扩展短信通知
这个巡检脚本可以根据您的具体需求进行调整和扩展。