本文目录导读:

我来详细介绍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']);
}
}
这个完整的告警系统实现了多渠道、限流、去重、升级等高级功能,你可以根据实际需求选择合适的方案进行部署。