本文目录导读:

- 核心思路
- 方案一:基于配置文件(简单、适用单机/小项目)
- 方案二:基于数据库/Redis(灵活、支持动态配置)
- 方案三:使用任务调度 + 开关(适合整站维护)
- 高级:基于告警聚合(De-duplication + Silence)
- 最佳实践建议
- 简单封装示例(即时可用)
在 PHP 项目中实现告警静默(时段屏蔽通知),通常是为了在维护窗口、非工作时间或已知故障期间避免不必要的告警骚扰,以下是几种常见的架构设计与实现方案,你可以根据项目复杂度选择。
核心思路
- 配置中心:定义静默规则(时间范围、周期、匹配条件)。
- 过滤器/中间件:在告警触发点(发送通知前)检查规则。
- 执行:如果匹配,则丢弃告警或标记为静默。
基于配置文件(简单、适用单机/小项目)
直接在项目配置中定义静默时段,使用数组或 .env 控制。
示例配置 (config/mute.php)
return [
'enabled' => true,
'rules' => [
[
'start' => '02:00',
'end' => '05:00',
'days' => ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], // 工作日
'match' => null, // null 表示匹配所有告警,或指定告警类名
],
[
'start' => '00:00',
'end' => '23:59',
'days' => ['Sat', 'Sun'],
'match' => 'critical', // 仅静默 critical 级别的告警
],
],
];
核心检查逻辑
class AlertMuteService
{
public function shouldMute(string $alert, string $level = null): bool
{
$config = config('mute');
if (!$config['enabled']) {
return false;
}
$now = Carbon::now();
$currentDay = $now->shortDayName; // Mon, Tue...
$currentTime = $now->format('H:i');
foreach ($config['rules'] as $rule) {
// 1. 检查日期
if (!in_array($currentDay, $rule['days'] ?? [])) {
continue;
}
// 2. 检查时间区间
if ($currentTime < $rule['start'] || $currentTime > $rule['end']) {
continue;
}
// 3. 检查匹配条件
if ($rule['match'] !== null) {
if ($rule['match'] === 'critical' && $level !== 'critical') {
continue;
}
// 也可以匹配告警名称 pattern
}
return true; // 命中静默规则
}
return false;
}
}
调用时机(发送通知前)
if (app(AlertMuteService::class)->shouldMute($alert, $level)) {
logger('Alert muted during silent period');
return;
}
// 正常发送邮件/短信/企业微信...
基于数据库/Redis(灵活、支持动态配置)
适用于需要运营后台动态修改静默时段的项目。
数据表结构(MySQL 示例)
CREATE TABLE `alert_mute_rules` (
`id` int PRIMARY KEY AUTO_INCREMENT,
`name` varchar(100) COMMENT '规则名称',
`time_zone` varchar(50) DEFAULT 'Asia/Shanghai',
`start_time` time NOT NULL,
`end_time` time NOT NULL,
`days_of_week` varchar(20) COMMENT '1-7,逗号分隔,如1,2,3,4,5',
`alert_type` varchar(50) DEFAULT NULL COMMENT 'null表示全部',
`alert_level` varchar(20) DEFAULT NULL COMMENT 'critical/warning/info',
`status` tinyint DEFAULT 1,
`created_at` timestamp
);
Redis 缓存预热(推荐)
- 将所有活跃规则保存到 Redis
Sorted Set或Hash中。 - 定时任务 (Cron) 每分钟检查当前时间是否命中规则。
- 或在发送告警时实时查询。
实时查询示例
class RedisAlertMuteChecker
{
public function isMuted($alert): bool
{
$now = new DateTime('now', new DateTimeZone('Asia/Shanghai'));
$time = $now->format('H:i');
$dayOfWeek = $now->format('N'); // 1-7
// 从 Redis 获取所有规则(建议用 Hash 存储,field为规则ID)
$rules = Redis::hGetAll('alert:mute:rules');
foreach ($rules as $rule) {
// 解析 JSON
$ruleData = json_decode($rule, true);
if (!$ruleData['status']) continue;
// 检查日期
$days = explode(',', $ruleData['days_of_week']);
if (!in_array($dayOfWeek, $days)) continue;
// 检查时间区间(支持跨天,需要特殊处理)
if ($time >= $ruleData['start_time'] && $time <= $ruleData['end_time']) {
// 匹配告警类型/级别
if ($ruleData['alert_type'] && $ruleData['alert_type'] !== $alert['type']) continue;
if ($ruleData['alert_level'] && $ruleData['alert_level'] !== $alert['level']) continue;
return true;
}
}
return false;
}
}
使用任务调度 + 开关(适合整站维护)
在项目本身进行“维护模式”时,同步静默所有告警。
方法:利用 Laravel 的 DownForMaintenance 或者项目自定义的全局变量。
if (app()->isDownForMaintenance()) {
// 直接返回,不发送任何通知
logger('Site under maintenance, alert muted');
return;
}
配合定时任务:
# 凌晨 2点进入静默 0 2 * * * php artisan mute:on --reason="系统维护" # 凌晨 5点恢复通知 0 5 * * * php artisan mute:off
高级:基于告警聚合(De-duplication + Silence)
如果使用 Prometheus + AlertManager 或 Grafana OnCall,在应用层之外还能做:
| 组件 | 静默功能 |
|---|---|
| AlertManager | 天然的 silences API,支持时间、标签匹配,php 应用只需发送告警到 AlertManager,静默由 AlertManager 维护。 |
| Grafana OnCall | 动态将告警静默、通知对接 |
| PHP + Webhook + CRON | 编写 shell 脚本定时调用 curl -XPOST alertmanager/silences |
最佳实践建议
- 不要在生产代码里 hardcode 时间:使用配置中心或数据库。
- 考虑时区:如果项目部署在全球,统一使用 UTC 存储,展示时转本地。
- 日志记录:静默的告警也要记录日志,方便事后排查。
- 跨天支持:
23:00 - 01:00的静默需要特殊逻辑。 - 优先级:如果存在多条规则,明确静默规则的优先级(如:更具体的规则优先级更高)。
简单封装示例(即时可用)
// services/AlertMuteService.php
class AlertMuteService
{
public function check(array $alert): bool
{
$config = config('alert.mute_rules', []);
$now = Carbon::now();
foreach ($config as $rule) {
// 解析时间
$start = Carbon::parse($rule['start']);
$end = Carbon::parse($rule['end']);
// 处理跨天
if ($start->greaterThan($end)) {
// 跨越午夜,如 23:00 -> 01:00
if (!($now->greaterThanOrEqualTo($start) || $now->lessThanOrEqualTo($end))) {
continue;
}
} else {
// 普通区间
if (!($now->between($start, $end))) {
continue;
}
}
// 检查星期
if (isset($rule['days']) && !in_array($now->dayOfWeek, $rule['days'])) continue;
// 检查告警级别
if (isset($rule['level']) && $rule['level'] !== $alert['level']) continue;
return true;
}
return false;
}
}
选择哪种方案取决于你的项目规模、告警系统架构、团队对运维的可控需求,推荐至少做到 配置化 + 数据库支持动态调整,便于快速响应线上变更。