本文目录导读:

实现PHP项目的用户自定义告警功能,通常需要覆盖规则配置、数据采集、逻辑判断、通知推送四个核心环节,下面提供一套完整的实现方案和代码示例。
整体架构设计
graph TD
A[用户端] -->|配置告警规则| B[规则存储]
B -->|读取规则| C[告警引擎]
D[数据源] -->|推送数据| C
C -->|触发条件| E[通知渠道]
E -->|发送消息| F[用户]
数据库表结构设计(MySQL)
告警规则表
CREATE TABLE `alert_rules` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '用户ID',
`rule_name` varchar(100) NOT NULL COMMENT '规则名称',
`metric` varchar(50) NOT NULL COMMENT '监控指标(如 cpu_usage, memory_usage, order_amount)',
`operator` enum('>','<','>=','<=','==','!=') NOT NULL COMMENT '比较运算符',
`threshold` decimal(20,6) NOT NULL COMMENT '阈值',
`duration` int(11) DEFAULT 0 COMMENT '持续次数 (N次持续触发才告警)',
`notification_method` json NOT NULL COMMENT '通知方式 ["email","sms","dingtalk","webhook"]',
`notification_target` json NOT NULL COMMENT '通知目标 {"email":"user@example.com","webhook":"https://.../"}',
`enabled` tinyint(1) DEFAULT 1 COMMENT '启用状态',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_metric` (`metric`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
告警事件记录表
CREATE TABLE `alert_events` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `rule_id` int(11) NOT NULL, `user_id` int(11) NOT NULL, `metric` varchar(50) NOT NULL, `triggered_value` decimal(20,6) NOT NULL COMMENT '触发时的实际值', `message` text COMMENT '告警消息内容', `notified` tinyint(1) DEFAULT 0 COMMENT '是否已通知', `notified_at` datetime DEFAULT NULL, `created_at` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_rule_id` (`rule_id`), KEY `idx_user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
核心代码实现
告警引擎类(AlertEngine.php)
<?php
namespace App\Service;
use App\Model\AlertRule;
use App\Model\AlertEvent;
use App\Notification\NotificationFactory;
class AlertEngine
{
private $thresholdCounter = []; // 存储连续满足条件次数
/**
* 处理新的监控数据点
* @param string $metric 指标名称
* @param float $value 当前值
* @param int $userId 数据所属用户(可选,如果不传则遍历所有规则)
*/
public function processDataPoint(string $metric, float $value, ?int $userId = null): void
{
// 1. 查询所有匹配该指标且启用的规则
$rules = AlertRule::query()
->where('metric', $metric)
->where('enabled', 1);
if ($userId !== null) {
$rules->where('user_id', $userId);
}
// 2. 逐条规则判断
foreach ($rules->cursor() as $rule) {
$this->evaluateRule($rule, $value);
}
}
/**
* 评估单条规则
*/
private function evaluateRule(AlertRule $rule, float $currentValue): void
{
// 对比运算
$triggered = false;
switch ($rule->operator) {
case '>':
$triggered = $currentValue > $rule->threshold;
break;
case '<':
$triggered = $currentValue < $rule->threshold;
break;
case '>=':
$triggered = $currentValue >= $rule->threshold;
break;
case '<=':
$triggered = $currentValue <= $rule->threshold;
break;
case '==':
$triggered = abs($currentValue - $rule->threshold) < 0.000001; // 浮点数比较
break;
case '!=':
$triggered = abs($currentValue - $rule->threshold) >= 0.000001;
break;
}
// 取连续触发计数器(以规则ID为key)
$key = $rule->id;
if (!isset($this->thresholdCounter[$key])) {
$this->thresholdCounter[$key] = 0;
}
if ($triggered) {
$this->thresholdCounter[$key]++;
// 连续触发达到设定次数
if ($this->thresholdCounter[$key] >= $rule->duration) {
$this->sendAlert($rule, $currentValue);
// 重置计数器,避免重复告警(可按需设计重试间隔)
$this->thresholdCounter[$key] = 0;
}
} else {
// 条件不满足则重置计数器
$this->thresholdCounter[$key] = 0;
}
}
/**
* 执行告警通知
*/
private function sendAlert(AlertRule $rule, float $value): void
{
// 1. 记录告警事件
$event = AlertEvent::create([
'rule_id' => $rule->id,
'user_id' => $rule->user_id,
'metric' => $rule->metric,
'triggered_value'=> $value,
'message' => "{$rule->rule_name} 触发告警: {$rule->metric} = {$value} {$rule->operator} {$rule->threshold}"
]);
// 2. 发送通知
$methods = json_decode($rule->notification_method, true) ?? [];
$targets = json_decode($rule->notification_target, true) ?? [];
foreach ($methods as $index => $method) {
$target = $targets[$index] ?? '';
if (empty($target)) continue;
$notifier = NotificationFactory::create($method);
$notifier->send(
$target,
$event->message,
[
'rule' => $rule->toArray(),
'triggered_value' => $value
]
);
}
// 3. 标记事件已通知
$event->update(['notified' => 1, 'notified_at' => now()]);
}
}
通知工厂(NotificationFactory.php)
<?php
namespace App\Notification;
class NotificationFactory
{
public static function create(string $type): NotificationInterface
{
switch ($type) {
case 'email':
return app(EmailNotifier::class);
case 'sms':
return app(SmsNotifier::class);
case 'dingtalk':
return app(DingTalkNotifier::class);
case 'webhook':
return app(WebhookNotifier::class);
default:
throw new \InvalidArgumentException("不支持的告警方式: {$type}");
}
}
}
// 通知接口
interface NotificationInterface
{
public function send(string $target, string $message, array $context = []): bool;
}
一个纯PHP的Webhook通知示例
<?php
namespace App\Notification;
class WebhookNotifier implements NotificationInterface
{
public function send(string $target, string $message, array $context = []): bool
{
$payload = [
'msgtype' => 'text',
'text' => [
'content' => $message
],
'context' => $context
];
$ch = curl_init($target);
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;
}
}
数据采集入口(可放在队列Job或定时任务中)
<?php
// 示例:每天定时检查数据库里的订单异常、或从Redis接收实时指标
use App\Service\AlertEngine;
// 在控制器里或队列消费者里调用
$engine = new AlertEngine();
// 场景1:推送实时数据点(例如从MQTT、Redis订阅接收)
$engine->processDataPoint('cpu_usage', 85.5, userId: 123);
// 场景2:批量处理(从数据库统计)
$metrics = [
['metric' => 'login_fail_count', 'value' => 10, 'user_id' => 456],
['metric' => 'disk_usage', 'value' => 92.3, 'user_id' => 789],
];
foreach ($metrics as $point) {
$engine->processDataPoint($point['metric'], $point['value'], $point['user_id']);
}
用户端配置页面(简单示例)
使用Bootstrap + axios 实现一个快速规则配置表单:
<form id="alertRuleForm">
<div class="form-group">
<label>规则名称</label>
<input type="text" name="rule_name" class="form-control" required>
</div>
<div class="form-group">
<label>监控指标</label>
<select name="metric" class="form-control">
<option value="cpu_usage">CPU使用率 (%)</option>
<option value="memory_usage">内存使用率 (%)</option>
<option value="api_error_rate">API错误率 (%)</option>
<option value="daily_order_amount">日订单金额</option>
</select>
</div>
<div class="form-group row">
<div class="col-4">
<label>操作符</label>
<select name="operator" class="form-control">
<option value=">">大于 ></option>
<option value="<">小于 <</option>
<option value=">=">大于等于 >=</option>
<option value="<=">小于等于 <=</option>
</select>
</div>
<div class="col-4">
<label>阈值</label>
<input type="number" name="threshold" step="0.01" class="form-control">
</div>
<div class="col-4">
<label>持续次数</label>
<input type="number" name="duration" min="0" class="form-control" value="1">
</div>
</div>
<div class="form-group">
<label>通知方式</label>
<div>
<label class="checkbox-inline"><input type="checkbox" name="methods[]" value="email"> 邮件</label>
<label class="checkbox-inline"><input type="checkbox" name="methods[]" value="dingtalk"> 钉钉</label>
<label class="checkbox-inline"><input type="checkbox" name="methods[]" value="webhook"> Webhook</label>
</div>
</div>
<div class="form-group">
<label>通知目标(根据方式填写邮箱/钉钉Token/Webhook URL)</label>
<textarea name="targets" class="form-control" rows="2"
placeholder='如: {"email":"admin@example.com", "webhook":"https://hook.xxx/abc"}'></textarea>
</div>
<button type="submit" class="btn btn-primary">保存规则</button>
</form>
<script>
document.getElementById('alertRuleForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
const data = {
rule_name: formData.get('rule_name'),
metric: formData.get('metric'),
operator: formData.get('operator'),
threshold: parseFloat(formData.get('threshold')),
duration: parseInt(formData.get('duration')),
notification_method: formData.getAll('methods[]'),
notification_target: JSON.parse(formData.get('targets') || '{}'),
enabled: 1
};
// 调后端API保存
axios.post('/api/alert-rules', data).then(res => {
alert('保存成功');
});
});
</script>
扩展建议
-
防重复告警:
- 记录每条规则最后一次告警时间,加上最小告警间隔(如 5 分钟)。
- 或者使用
alert_events表判断同规则 5 分钟内是否已触发。
-
批量处理:
- 建议使用队列(如 Redis + Laravel Queue)异步处理通知,避免阻塞主流程。
- 使用
chunk分页加载规则,防止规则过多内存溢出。
-
聚合告警:
- 支持多个条件组合(AND/OR),需要将条件存储在子表中。
- 支持 “一次告警” / “恢复告警” 两种状态。
-
安全考虑:
- 用户输入的 Webhook URL 应该做白名单或限制协议(仅 HTTPS)。
- 对用户输入的所有字段做权限校验(防止越权创建他人规则)。
-
监控同步:
如果数据点来自外部系统(Prometheus、CloudWatch等),可以开发适配器,将外部指标推送到该告警引擎。
如何集成到现有项目
| 步骤 | 操作 |
|---|---|
| 1 | 执行SQL创建上述两张表 |
| 2 | 复制 AlertEngine.php、NotificationFactory.php、各通知实现类到项目中 |
| 3 | 编写用户管理告警规则的 CRUD API和前端页面 |
| 4 | 在数据采集或定时任务中调用 $engine->processDataPoint(...) |
| 5 | 定期清理过期告警事件(可选) |
这套方案覆盖了80%的Web项目自定义告警需求,你可以根据你的监控数据的频率(实时 / 每分钟 / 每天)选择同步推送或定时批量执行。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播