如何用PHP项目实现告警通知?

wen java案例 1

本文目录导读:

如何用PHP项目实现告警通知?

  1. 基础架构设计
  2. 代码实现方案
  3. 高级功能实现
  4. 最佳实践建议

我来详细介绍PHP项目实现告警通知的几种主流方案。

基础架构设计

告警系统核心组件

告警源 → 告警判断 → 告警队列 → 通知渠道 → 用户

代码实现方案

邮件告警(最基础)

<?php
// EmailAlert.php
class EmailAlert {
    private $to;
    private $subject;
    private $headers;
    public function __construct($to, $from = 'alert@yourdomain.com') {
        $this->to = $to;
        $this->headers = "From: $from\r\n";
        $this->headers .= "Content-Type: text/html; charset=UTF-8\r\n";
    }
    public function sendAlert($level, $message, $data = []) {
        $subject = "[{$level}] 系统告警通知";
        $body = $this->buildEmailBody($level, $message, $data);
        return mail($this->to, $subject, $body, $this->headers);
    }
    private function buildEmailBody($level, $message, $data) {
        $html = "<h2>系统告警通知</h2>";
        $html .= "<p><strong>级别:</strong> {$level}</p>";
        $html .= "<p><strong>时间:</strong> " . date('Y-m-d H:i:s') . "</p>";
        $html .= "<p><strong>消息:</strong> {$message}</p>";
        if (!empty($data)) {
            $html .= "<h3>详细信息:</h3><pre>" . json_encode($data, JSON_PRETTY_PRINT) . "</pre>";
        }
        return $html;
    }
}

短信告警(使用阿里云SMS)

<?php
// SmsAlert.php
require_once 'vendor/autoload.php';
use AlibabaCloud\Client\AlibabaCloud;
use AlibabaCloud\Client\Exception\ClientException;
use AlibabaCloud\Client\Exception\ServerException;
class SmsAlert {
    private $accessKeyId;
    private $accessKeySecret;
    private $signName;
    private $templateCode;
    public function __construct($config) {
        $this->accessKeyId = $config['access_key_id'];
        $this->accessKeySecret = $config['access_key_secret'];
        $this->signName = $config['sign_name'];
        $this->templateCode = $config['template_code'];
        AlibabaCloud::accessKeyClient(
            $this->accessKeyId,
            $this->accessKeySecret
        )->regionId('cn-hangzhou')->asDefaultClient();
    }
    public function sendSms($phoneNumbers, $level, $message) {
        try {
            $result = AlibabaCloud::rpc()
                ->product('Dysmsapi')
                ->version('2017-05-25')
                ->action('SendSms')
                ->method('POST')
                ->options([
                    'query' => [
                        'PhoneNumbers' => $phoneNumbers,
                        'SignName' => $this->signName,
                        'TemplateCode' => $this->templateCode,
                        'TemplateParam' => json_encode([
                            'level' => $level,
                            'message' => $message,
                            'time' => date('H:i:s')
                        ]),
                    ],
                ])
                ->request();
            return $result->toArray();
        } catch (ClientException $e) {
            throw new Exception("发送短信失败: " . $e->getErrorMessage());
        } catch (ServerException $e) {
            throw new Exception("服务器错误: " . $e->getErrorMessage());
        }
    }
}

企业微信/钉钉机器人告警

<?php
// WebhookAlert.php
class WebhookAlert {
    private $webhookUrl;
    private $type; // 'wechat' or 'dingtalk'
    public function __construct($webhookUrl, $type = 'wechat') {
        $this->webhookUrl = $webhookUrl;
        $this->type = $type;
    }
    public function sendAlert($level, $message, $data = []) {
        $payload = $this->buildPayload($level, $message, $data);
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->webhookUrl);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        return $httpCode === 200;
    }
    private function buildPayload($level, $message, $data) {
        if ($this->type === 'dingtalk') {
            return [
                'msgtype' => 'markdown',
                'markdown' => [
                    'title' => "【{$level}】系统告警通知",
                    'text' => "### 系统告警通知\n" .
                             "**级别:** {$level}\n" .
                             "**时间:** " . date('Y-m-d H:i:s') . "\n" .
                             "**消息:** {$message}\n" .
                             (!empty($data) ? "**详细信息:** \n```json\n" . 
                              json_encode($data, JSON_PRETTY_PRINT) . "\n```" : "")
                ]
            ];
        } else {
            // 企业微信格式
            $content = "[{$level}] {$message}\n时间: " . date('Y-m-d H:i:s');
            return [
                'msgtype' => 'text',
                'text' => [
                    'content' => $content
                ]
            ];
        }
    }
}

告警管理器(统一管理)

<?php
// AlertManager.php
class AlertManager {
    private $channels = [];
    private $alertHistory = [];
    private $throttleConfig = [];
    public function addChannel($name, $channel) {
        $this->channels[$name] = $channel;
        $this->throttleConfig[$name] = [
            'last_sent' => 0,
            'count' => 0,
            'max_per_minute' => 10
        ];
    }
    public function sendAlert($level, $message, $data = [], $channels = []) {
        $alertId = $this->generateAlertId();
        $this->alertHistory[$alertId] = [
            'level' => $level,
            'message' => $message,
            'data' => $data,
            'time' => time()
        ];
        $results = [];
        // 如果没有指定通道,发送到所有通道
        if (empty($channels)) {
            $channels = array_keys($this->channels);
        }
        foreach ($channels as $channelName) {
            if (!isset($this->channels[$channelName])) {
                $results[$channelName] = false;
                continue;
            }
            // 限流检查
            if (!$this->checkThrottle($channelName)) {
                $results[$channelName] = false;
                continue;
            }
            try {
                $result = $this->channels[$channelName]->sendAlert(
                    $level, 
                    $message, 
                    $data
                );
                $results[$channelName] = $result;
                // 记录发送状态
                $this->logAlert($alertId, $channelName, $result);
            } catch (Exception $e) {
                $results[$channelName] = false;
                $this->logError($channelName, $e->getMessage());
            }
        }
        return $results;
    }
    private function checkThrottle($channelName) {
        $currentTime = time();
        $config = $this->throttleConfig[$channelName];
        // 重置计数器
        if ($currentTime - $config['last_sent'] >= 60) {
            $this->throttleConfig[$channelName] = [
                'last_sent' => $currentTime,
                'count' => 0,
                'max_per_minute' => $config['max_per_minute']
            ];
        }
        // 检查是否超过限制
        if ($config['count'] >= $config['max_per_minute']) {
            return false;
        }
        $this->throttleConfig[$channelName]['count']++;
        return true;
    }
    private function generateAlertId() {
        return md5(uniqid() . time() . rand());
    }
    private function logAlert($alertId, $channel, $success) {
        $logEntry = [
            'alert_id' => $alertId,
            'channel' => $channel,
            'success' => $success,
            'time' => date('Y-m-d H:i:s')
        ];
        // 写入日志文件
        file_put_contents(
            '/var/log/alert_history.log',
            json_encode($logEntry) . PHP_EOL,
            FILE_APPEND
        );
    }
    private function logError($channel, $errorMessage) {
        file_put_contents(
            '/var/log/alert_errors.log',
            date('Y-m-d H:i:s') . " - {$channel}: {$errorMessage}" . PHP_EOL,
            FILE_APPEND
        );
    }
}

使用场景示例

<?php
// 使用示例
require_once 'AlertManager.php';
require_once 'EmailAlert.php';
require_once 'WebhookAlert.php';
// 1. 初始化告警管理器
$alertManager = new AlertManager();
// 2. 配置告警通道
$alertManager->addChannel('email', new EmailAlert('admin@example.com'));
$alertManager->addChannel('wechat', new WebhookAlert(
    'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=your-key',
    'wechat'
));
$alertManager->addChannel('dingtalk', new WebhookAlert(
    'https://oapi.dingtalk.com/robot/send?access_token=your-token',
    'dingtalk'
));
// 3. 业务监控中的使用
class BusinessMonitor {
    private $alertManager;
    public function __construct($alertManager) {
        $this->alertManager = $alertManager;
    }
    // 监控数据库连接
    public function checkDatabaseConnection() {
        try {
            $db = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
            // 模拟连接成功
            return true;
        } catch (PDOException $e) {
            $this->alertManager->sendAlert(
                'CRITICAL',
                "数据库连接失败: " . $e->getMessage(),
                ['host' => 'localhost', 'error_code' => $e->getCode()],
                ['email', 'wechat'] // 发送到指定通道
            );
            return false;
        }
    }
    // 监控服务器资源
    public function checkServerResources() {
        $cpuUsage = sys_getloadavg()[0];
        $memoryUsage = $this->getMemoryUsage();
        // CPU使用率超过80%告警
        if ($cpuUsage > 0.8) {
            $this->alertManager->sendAlert(
                'WARNING',
                "CPU使用率过高: " . round($cpuUsage * 100) . "%",
                ['cpu_usage' => $cpuUsage, 'threshold' => 0.8],
                ['dingtalk']
            );
        }
        // 内存使用率超过90%告警
        if ($memoryUsage > 0.9) {
            $this->alertManager->sendAlert(
                'CRITICAL',
                "内存使用率过高: " . round($memoryUsage * 100) . "%",
                ['memory_usage' => $memoryUsage, 'threshold' => 0.9],
                ['wechat', 'email']
            );
        }
    }
    private function getMemoryUsage() {
        $free = shell_exec('free');
        $free = (string)trim($free);
        $freeArr = explode("\n", $free);
        $mem = explode(" ", $freeArr[1]);
        $mem = array_filter($mem);
        $mem = array_merge($mem);
        $memoryUsage = $mem[2] / $mem[1];
        return $memoryUsage;
    }
}
// 4. 定时任务中使用(cron)
// 创建监控实例
$monitor = new BusinessMonitor($alertManager);
// 执行监控检查
$monitor->checkDatabaseConnection();
$monitor->checkServerResources();

高级功能实现

告警聚合与去重

<?php
// AlertDeduplicator.php
class AlertDeduplicator {
    private $redis;
    private $window = 300; // 5分钟窗口
    public function __construct($redis) {
        $this->redis = $redis;
    }
    public function isDuplicate($alertKey, $threshold = 3) {
        $key = "alert:{$alertKey}";
        $count = $this->redis->incr($key);
        if ($count === 1) {
            $this->redis->expire($key, $this->window);
            return false;
        }
        return $count > $threshold;
    }
}

告警升级机制

<?php
// EscalationManager.php
class EscalationManager {
    private $levels = [
        'info' => ['email' => ['admin@example.com']],
        'warning' => ['email' => ['admin@example.com', 'team@example.com']],
        'critical' => [
            'email' => ['admin@example.com', 'team@example.com', 'manager@example.com'],
            'sms' => ['13800138000'],
            'phone' => ['010-88888888']
        ]
    ];
    public function escalate($alert, $currentAttempt) {
        $level = $alert['level'];
        // 根据尝试次数升级告警级别
        if ($currentAttempt >= 3 && $level === 'warning') {
            $alert['level'] = 'critical';
        }
        return $alert;
    }
}

最佳实践建议

配置管理

// config/alert.php
return [
    'channels' => [
        'email' => [
            'class' => EmailAlert::class,
            'config' => [
                'to' => ['admin@example.com', 'ops@example.com'],
                'from' => 'alert@yourdomain.com'
            ]
        ],
        'dingtalk' => [
            'class' => WebhookAlert::class,
            'config' => [
                'url' => 'https://oapi.dingtalk.com/robot/send?access_token=xxx',
                'type' => 'dingtalk'
            ]
        ]
    ],
    'throttle' => [
        'max_per_minute' => 10,
        'dedup_window' => 300 // seconds
    ],
    'rules' => [
        'db_connection' => ['level' => 'critical', 'channels' => ['email', 'dingtalk']],
        'high_cpu' => ['level' => 'warning', 'channels' => ['dingtalk']]
    ]
];

测试建议

// tests/AlertTest.php
class AlertTest extends PHPUnit\Framework\TestCase {
    public function testAlertManager() {
        $alertManager = new AlertManager();
        $mockChannel = $this->createMock(EmailAlert::class);
        $mockChannel->expects($this->once())
            ->method('sendAlert')
            ->willReturn(true);
        $alertManager->addChannel('test', $mockChannel);
        $result = $alertManager->sendAlert(
            'CRITICAL',
            'Test alert',
            ['test' => true],
            ['test']
        );
        $this->assertTrue($result['test']);
    }
}

这个完整的告警系统实现了多渠道、限流、去重、升级等高级功能,你可以根据实际需求选择合适的方案进行部署。

抱歉,评论功能暂时关闭!