第7讲:实时告警与自动化响应

一、为什么需要实时告警与自动化响应

前三讲我们建立了监控体系,收集了大量数据。但如果没有人看这些数据,它们就是数字垃圾。

复制代码
❌ 常见问题:

1. 告警太多 → 狼来了效应
   凌晨3点收到 50 条告警,全是 INFO 级别的噪音
   
2. 告警太慢 → 用户先发现
   用户投诉了才发现 QPS 掉了一半

3. 告警没上下文 → 不知道怎么办
   收到 "ERROR rate > 5%" 的告警
   但不知道是哪个服务、哪个接口、哪个用户

4. 告警没响应 → 告了也白告
   告警发了,没人处理,半小时后还是同样的告警

5. 告警风暴 → 系统被自己打死
   一台机器挂了,触发 100 条告警
   100 条告警又触发 1000 条通知
   告警系统把自己打挂了

解决方案:分级告警 + 自动化响应 + 风暴抑制

复制代码
✅ 分级:P0-P3,不同级别不同响应方式
✅ 聚合:相同原因的告警合并成一条
✅ 抑制:已知故障不再重复告警
✅ 自动:常见问题自动恢复,无需人工
✅ 上下文:告警附带根因分析和修复建议

二、架构设计

复制代码
┌─────────────────────────────────────────────────────────────┐
│                    告警流水线                                  │
│                                                             │
│  数据源                                                     │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐                  │
│  │ Metrics  │  │  Logs    │  │  Traces  │                  │
│  └────┬─────┘  └────┬─────┘  └────┬─────┘                  │
│       │              │             │                        │
│       └──────────────┼─────────────┘                        │
│                      ▼                                      │
│  ┌──────────────────────────────────────────┐               │
│  │         Rule Engine (规则引擎)             │               │
│  │  ├─ 阈值规则: CPU > 90%                   │               │
│  │  ├─ 趋势规则: 错误率持续上升                │               │
│  │  ├─ 同比规则: 相比昨天同时间增长 50%        │               │
│  │  └─ 复合规则: 错误率高 + QPS 下降          │               │
│  └──────────────────┬───────────────────────┘               │
│                     │                                       │
│  ┌──────────────────▼───────────────────────┐               │
│  │      Alert Manager (告警管理器)            │               │
│  │  ├─ 分级:P0(立即) / P1(5min) / P2(30min) │               │
│  │  ├─ 聚合:相同指纹合并                     │               │
│  │  ├─ 抑制:已知故障不重复                   │               │
│  │  └─ 静默:维护窗口不告警                   │               │
│  └──────────────────┬───────────────────────┘               │
│                     │                                       │
│  ┌──────────────────▼───────────────────────┐               │
│  │   Auto Responder (自动化响应)              │               │
│  │  ├─ 自动恢复:重启/扩容/回滚               │               │
│  │  ├─ 自动降级:熔断/限流/降级               │               │
│  │  └─ 通知渠道:钉钉/企微/短信/电话          │               │
│  └──────────────────────────────────────────┘               │
└─────────────────────────────────────────────────────────────┘

三、完整代码实现

复制代码
package main

import (
	"encoding/json"
	"fmt"
	"math"
	"sort"
	"strings"
	"sync"
	"time"
)

// ============================================================
// 1. 核心数据结构
// ============================================================

// AlertLevel 告警级别
type AlertLevel int

const (
	AlertP0 AlertLevel = iota // 致命:立即处理,电话通知
	AlertP1                   // 严重:5分钟内处理
	AlertP2                   // 警告:30分钟内处理
	AlertP3                   // 通知:下一个工作日处理
)

func (l AlertLevel) String() string {
	switch l {
	case AlertP0:
		return "P0 🔴"
	case AlertP1:
		return "P1 🟠"
	case AlertP2:
		return "P2 🟡"
	case AlertP3:
		return "P3 🔵"
	default:
		return "UNKNOWN"
	}
}

// AlertStatus 告警状态
type AlertStatus string

const (
	AlertStatusFiring   AlertStatus = "firing"
	AlertStatusResolved AlertStatus = "resolved"
	AlertStatusAcknowledged AlertStatus = "acknowledged"
	AlertStatusSuppressed AlertStatus = "suppressed"
)

// Alert 告警事件
type Alert struct {
	ID          string                 `json:"id"`
	Fingerprint string                 `json:"fingerprint"` // 用于去重的指纹
	Level       AlertLevel             `json:"level"`
	Title       string                 `json:"title"`
	Description string                 `json:"description"`
	Source      string                 `json:"source"` // metrics/logs/traces
	Labels      map[string]string      `json:"labels"`
	Annotations map[string]string      `json:"annotations"` // 附加信息
	Value       float64                `json:"value"`
	Threshold   float64                `json:"threshold"`
	StartedAt   time.Time              `json:"started_at"`
	UpdatedAt   time.Time              `json:"updated_at"`
	ResolvedAt  *time.Time             `json:"resolved_at,omitempty"`
	Status      AlertStatus            `json:"status"`
	AckBy       string                 `json:"ack_by,omitempty"`
	AutoAction  *AutoAction            `json:"auto_action,omitempty"`
}

// AutoAction 自动化响应动作
type AutoAction struct {
	ActionType string                 `json:"action_type"` // restart/scale/rollback/circuit_breaker
	Target     string                 `json:"target"`
	Params     map[string]interface{} `json:"params"`
	ExecutedAt time.Time              `json:"executed_at"`
	Success    bool                   `json:"success"`
	Result     string                 `json:"result"`
}

// AlertRule 告警规则
type AlertRule struct {
	ID          string            `json:"id"`
	Name        string            `json:"name"`
	Description string            `json:"description"`
	Level       AlertLevel        `json:"level"`
	Enabled     bool              `json:"enabled"`
	
	// 规则条件
	MetricName  string            `json:"metric_name"`
	Operator    string            `json:"operator"` // > >= < <= == !=
	Threshold   float64           `json:"threshold"`
	Duration    time.Duration     `json:"duration"` // 持续多久才触发
	
	// 复合条件
	Conditions  []RuleCondition   `json:"conditions,omitempty"`
	
	// 标签和注解
	Labels      map[string]string `json:"labels"`
	Annotations map[string]string `json:"annotations"`
	
	// 自动化响应
	AutoActions []AutoActionDef   `json:"auto_actions,omitempty"`
	
	// 静默配置
	SilencePeriods []SilencePeriod `json:"silence_periods,omitempty"`
	
	// 统计
	TriggeredCount int64           `json:"triggered_count"`
	LastTriggered  *time.Time      `json:"last_triggered"`
}

type RuleCondition struct {
	MetricName string  `json:"metric_name"`
	Operator   string  `json:"operator"`
	Threshold  float64 `json:"threshold"`
}

type AutoActionDef struct {
	ActionType string                 `json:"action_type"`
	Target     string                 `json:"target"`
	Cooldown   time.Duration          `json:"cooldown"` // 冷却期
	Params     map[string]interface{} `json:"params"`
}

type SilencePeriod struct {
	Start string `json:"start"` // HH:MM
	End   string `json:"end"`   // HH:MM
	Weekdays []time.Weekday `json:"weekdays"`
	Reason string `json:"reason"`
}

// MetricSample 指标样本
type MetricSample struct {
	Name      string            `json:"name"`
	Value     float64           `json:"value"`
	Timestamp time.Time         `json:"timestamp"`
	Labels    map[string]string `json:"labels"`
}

// ============================================================
// 2. 规则引擎
// ============================================================

type RuleEngine struct {
	mu    sync.RWMutex
	rules map[string]*AlertRule
	
	// 指标历史(用于趋势检测)
	metricHistory map[string][]*MetricSample
	historyMax    int
}

func NewRuleEngine() *RuleEngine {
	return &RuleEngine{
		rules:         make(map[string]*AlertRule),
		metricHistory: make(map[string][]*MetricSample),
		historyMax:    1000,
	}
}

// RegisterRule 注册告警规则
func (e *RuleEngine) RegisterRule(rule *AlertRule) {
	e.mu.Lock()
	defer e.mu.Unlock()
	e.rules[rule.ID] = rule
	fmt.Printf("[RuleEngine] 注册规则: %s (%s)\n", rule.Name, rule.Level)
}

// Evaluate 评估指标,返回触发的告警
func (e *RuleEngine) Evaluate(sample *MetricSample) []*Alert {
	e.mu.Lock()
	defer e.mu.Unlock()

	// 保存历史
	key := metricKey(sample.Name, sample.Labels)
	e.metricHistory[key] = append(e.metricHistory[key], sample)
	if len(e.metricHistory[key]) > e.historyMax {
		e.metricHistory[key] = e.metricHistory[key][len(e.metricHistory[key])-e.historyMax:]
	}

	// 评估所有规则
	alerts := make([]*Alert, 0)
	for _, rule := range e.rules {
		if !rule.Enabled {
			continue
		}

		// 检查静默期
		if e.isSilenced(rule) {
			continue
		}

		// 检查是否匹配此指标
		if !e.matchesMetric(rule, sample) {
			continue
		}

		// 检查条件
		if e.evaluateRule(rule, sample) {
			alert := e.buildAlert(rule, sample)
			alerts = append(alerts, alert)
			
			rule.TriggeredCount++
			now := time.Now()
			rule.LastTriggered = &now
		}
	}

	return alerts
}

func (e *RuleEngine) matchesMetric(rule *AlertRule, sample *MetricSample) bool {
	if rule.MetricName != "" && rule.MetricName != sample.Name {
		return false
	}
	
	// 检查标签匹配
	for k, v := range rule.Labels {
		if sample.Labels[k] != v {
			return false
		}
	}
	
	return true
}

func (e *RuleEngine) evaluateRule(rule *AlertRule, sample *MetricSample) bool {
	// 简单阈值
	if rule.Operator != "" {
		return e.compare(sample.Value, rule.Operator, rule.Threshold)
	}
	
	// 复合条件
	if len(rule.Conditions) > 0 {
		for _, cond := range rule.Conditions {
			key := metricKey(cond.MetricName, sample.Labels)
			history := e.metricHistory[key]
			if len(history) == 0 {
				return false
			}
			latest := history[len(history)-1]
			if !e.compare(latest.Value, cond.Operator, cond.Threshold) {
				return false
			}
		}
		return true
	}
	
	return false
}

func (e *RuleEngine) compare(value float64, operator string, threshold float64) bool {
	switch operator {
	case ">":
		return value > threshold
	case ">=":
		return value >= threshold
	case "<":
		return value < threshold
	case "<=":
		return value <= threshold
	case "==":
		return math.Abs(value-threshold) < 0.0001
	case "!=":
		return math.Abs(value-threshold) >= 0.0001
	default:
		return false
	}
}

func (e *RuleEngine) isSilenced(rule *AlertRule) bool {
	now := time.Now()
	weekday := now.Weekday()
	hour, min := now.Hour(), now.Minute()
	currentMinutes := hour*60 + min

	for _, sp := range rule.SilencePeriods {
		// 检查星期
		inWeekday := false
		for _, wd := range sp.Weekdays {
			if wd == weekday {
				inWeekday = true
				break
			}
		}
		if !inWeekday {
			continue
		}

		// 解析时间
		startParts := strings.Split(sp.Start, ":")
		endParts := strings.Split(sp.End, ":")
		if len(startParts) != 2 || len(endParts) != 2 {
			continue
		}
		
		startMin := atoi(startParts[0])*60 + atoi(startParts[1])
		endMin := atoi(endParts[0])*60 + atoi(endParts[1])

		if currentMinutes >= startMin && currentMinutes <= endMin {
			return true
		}
	}
	return false
}

func (e *RuleEngine) buildAlert(rule *AlertRule, sample *MetricSample) *Alert {
	fingerprint := fmt.Sprintf("%s:%s:%s", rule.ID, sample.Name, labelsToString(sample.Labels))
	
	return &Alert{
		ID:          fmt.Sprintf("alert-%x", time.Now().UnixNano()),
		Fingerprint: fingerprint,
		Level:       rule.Level,
		Title:       rule.Name,
		Description: fmt.Sprintf("%s = %.2f (阈值: %s %.2f)", 
			sample.Name, sample.Value, rule.Operator, rule.Threshold),
		Source:      "metrics",
		Labels:      sample.Labels,
		Annotations: rule.Annotations,
		Value:       sample.Value,
		Threshold:   rule.Threshold,
		StartedAt:   time.Now(),
		UpdatedAt:   time.Now(),
		Status:      AlertStatusFiring,
	}
}

// ============================================================
// 3. 告警管理器
// ============================================================

type AlertManager struct {
	mu       sync.Mutex
	alerts   map[string]*Alert // id -> alert
	rules    *RuleEngine
	
	// 去重
	firingAlerts map[string]*Alert // fingerprint -> alert
	
	// 抑制
	suppressionRules []SuppressionRule
	
	// 通知渠道
	notifiers []Notifier
}

type SuppressionRule struct {
	Name        string            `json:"name"`
	SourceMatch map[string]string `json:"source_match"` // 匹配源告警
	TargetMatch map[string]string `json:"target_match"` // 匹配目标告警
}

type Notifier interface {
	Send(alert *Alert) error
	Name() string
}

// ProcessAlerts 处理告警
func (am *AlertManager) ProcessAlerts(alerts []*Alert) {
	am.mu.Lock()
	defer am.mu.Unlock()

	for _, alert := range alerts {
		// 去重:相同指纹的告警,只保留最新的
		if existing, ok := am.firingAlerts[alert.Fingerprint]; ok {
			existing.UpdatedAt = time.Now()
			existing.Value = alert.Value
			continue
		}

		// 抑制检查
		if am.isSuppressed(alert) {
			alert.Status = AlertStatusSuppressed
			fmt.Printf("[AlertManager] 抑制告警: %s\n", alert.Title)
			continue
		}

		// 保存
		am.alerts[alert.ID] = alert
		am.firingAlerts[alert.Fingerprint] = alert

		// 发送通知
		am.notify(alert)
	}
}

func (am *AlertManager) isSuppressed(alert *Alert) bool {
	for _, sr := range am.suppressionRules {
		// 检查是否有匹配的源告警正在 firing
		for _, existing := range am.firingAlerts {
			if matchesLabels(existing.Labels, sr.SourceMatch) {
				if matchesLabels(alert.Labels, sr.TargetMatch) {
					return true
				}
			}
		}
	}
	return false
}

func (am *AlertManager) notify(alert *Alert) {
	for _, notifier := range am.notifiers {
		if err := notifier.Send(alert); err != nil {
			fmt.Printf("[AlertManager] %s 通知失败: %v\n", notifier.Name(), err)
		}
	}
}

// Acknowledge 确认告警
func (am *AlertManager) Acknowledge(alertID, user string) error {
	am.mu.Lock()
	defer am.mu.Unlock()

	alert, ok := am.alerts[alertID]
	if !ok {
		return fmt.Errorf("告警 %s 不存在", alertID)
	}

	alert.Status = AlertStatusAcknowledged
	alert.AckBy = user
	fmt.Printf("[AlertManager] %s 确认告警: %s\n", user, alert.Title)
	return nil
}

// Resolve 解决告警
func (am *AlertManager) Resolve(alertID string) error {
	am.mu.Lock()
	defer am.mu.Unlock()

	alert, ok := am.alerts[alertID]
	if !ok {
		return fmt.Errorf("告警 %s 不存在", alertID)
	}

	now := time.Now()
	alert.Status = AlertStatusResolved
	alert.ResolvedAt = &now
	
	delete(am.firingAlerts, alert.Fingerprint)
	
	fmt.Printf("[AlertManager] 告警已解决: %s (持续 %v)\n", 
		alert.Title, now.Sub(alert.StartedAt))
	return nil
}

// ============================================================
// 4. 自动化响应
// ============================================================

type AutoResponder struct {
	mu          sync.Mutex
	actions     map[string]*AutoAction
	actionLog   []*AutoActionResult
	cooldowns   map[string]time.Time // action_type:target -> cooldown until
}

type AutoActionResult struct {
	AlertID    string    `json:"alert_id"`
	Action     *AutoAction `json:"action"`
	StartedAt  time.Time `json:"started_at"`
	FinishedAt time.Time `json:"finished_at"`
	Success    bool      `json:"success"`
	Output     string    `json:"output"`
}

func NewAutoResponder() *AutoResponder {
	return &AutoResponder{
		actions:   make(map[string]*AutoAction),
		actionLog: make([]*AutoActionResult, 0),
		cooldowns: make(map[string]time.Time),
	}
}

// Execute 执行自动化响应
func (ar *AutoResponder) Execute(alert *Alert, actionDef AutoActionDef) *AutoActionResult {
	ar.mu.Lock()
	defer ar.mu.Unlock()

	// 检查冷却期
	cooldownKey := fmt.Sprintf("%s:%s", actionDef.ActionType, actionDef.Target)
	if until, ok := ar.cooldowns[cooldownKey]; ok {
		if time.Now().Before(until) {
			return &AutoActionResult{
				AlertID: alert.ID,
				Action:  &AutoAction{ActionType: actionDef.ActionType, Target: actionDef.Target},
				Success: false,
				Output:  fmt.Sprintf("冷却中,剩余 %v", until.Sub(time.Now())),
			}
		}
	}

	result := &AutoActionResult{
		AlertID:   alert.ID,
		StartedAt: time.Now(),
	}

	action := &AutoAction{
		ActionType: actionDef.ActionType,
		Target:     actionDef.Target,
		Params:     actionDef.Params,
		ExecutedAt: time.Now(),
	}

	// 执行动作
	switch actionDef.ActionType {
	case "restart":
		action.Success, action.Result = ar.restart(actionDef.Target)
	case "scale_up":
		action.Success, action.Result = ar.scaleUp(actionDef.Target, actionDef.Params)
	case "rollback":
		action.Success, action.Result = ar.rollback(actionDef.Target)
	case "circuit_breaker":
		action.Success, action.Result = ar.circuitBreaker(actionDef.Target, actionDef.Params)
	case "rate_limit":
		action.Success, action.Result = ar.rateLimit(actionDef.Target, actionDef.Params)
	default:
		action.Success = false
		action.Result = fmt.Sprintf("未知动作类型: %s", actionDef.ActionType)
	}

	result.Action = action
	result.FinishedAt = time.Now()
	result.Success = action.Success
	result.Output = action.Result

	alert.AutoAction = action

	// 设置冷却期
	ar.cooldowns[cooldownKey] = time.Now().Add(actionDef.Cooldown)

	// 记录日志
	ar.actionLog = append(ar.actionLog, result)

	fmt.Printf("[AutoResponder] 执行 %s(%s): %v\n", 
		actionDef.ActionType, actionDef.Target, action.Success)
	if !action.Success {
		fmt.Printf("  失败原因: %s\n", action.Result)
	}

	return result
}

func (ar *AutoResponder) restart(target string) (bool, string) {
	fmt.Printf("  ▶ 重启服务: %s\n", target)
	time.Sleep(100 * time.Millisecond) // 模拟重启
	return true, fmt.Sprintf("服务 %s 已重启", target)
}

func (ar *AutoResponder) scaleUp(target string, params map[string]interface{}) (bool, string) {
	replicas := 2
	if v, ok := params["replicas"]; ok {
		replicas = int(v.(float64))
	}
	fmt.Printf("  ▶ 扩容服务: %s → %d 副本\n", target, replicas)
	time.Sleep(200 * time.Millisecond)
	return true, fmt.Sprintf("%s 已扩容到 %d 副本", target, replicas)
}

func (ar *AutoResponder) rollback(target string) (bool, string) {
	fmt.Printf("  ▶ 回滚服务: %s\n", target)
	time.Sleep(150 * time.Millisecond)
	return true, fmt.Sprintf("%s 已回滚到上一版本", target)
}

func (ar *AutoResponder) circuitBreaker(target string, params map[string]interface{}) (bool, string) {
	duration := 30 * time.Second
	if v, ok := params["duration"]; ok {
		duration = time.Duration(v.(float64)) * time.Second
	}
	fmt.Printf("  ▶ 熔断服务: %s, 持续时间: %v\n", target, duration)
	time.Sleep(50 * time.Millisecond)
	return true, fmt.Sprintf("%s 已熔断 %v", target, duration)
}

func (ar *AutoResponder) rateLimit(target string, params map[string]interface{}) (bool, string) {
	qps := 100.0
	if v, ok := params["qps"]; ok {
		qps = v.(float64)
	}
	fmt.Printf("  ▶ 限流服务: %s → %v QPS\n", target, qps)
	time.Sleep(50 * time.Millisecond)
	return true, fmt.Sprintf("%s 已限流到 %v QPS", target, qps)
}

// ============================================================
// 5. 通知渠道
// ============================================================

// ConsoleNotifier 控制台通知(开发环境)
type ConsoleNotifier struct{}

func (n *ConsoleNotifier) Name() string { return "console" }

func (n *ConsoleNotifier) Send(alert *Alert) error {
	fmt.Printf("\n🚨 [通知] %s %s\n", alert.Level, alert.Title)
	fmt.Printf("   描述: %s\n", alert.Description)
	fmt.Printf("   值: %.2f (阈值: %.2f)\n", alert.Value, alert.Threshold)
	fmt.Printf("   时间: %s\n", alert.StartedAt.Format("15:04:05"))
	
	if alert.AutoAction != nil {
		fmt.Printf("   自动响应: %s → %s\n", alert.AutoAction.ActionType, alert.AutoAction.Result)
	}
	
	return nil
}

// DingTalkNotifier 钉钉通知(模拟)
type DingTalkNotifier struct {
	WebhookURL string
}

func (n *DingTalkNotifier) Name() string { return "dingtalk" }

func (n *DingTalkNotifier) Send(alert *Alert) error {
	msg := fmt.Sprintf(`【%s】%s
描述: %s
当前值: %.2f
阈值: %.2f
时间: %s`,
		alert.Level, alert.Title, alert.Description,
		alert.Value, alert.Threshold,
		alert.StartedAt.Format("2006-01-02 15:04:05"))

	_ = msg // 模拟发送
	return nil
}

// PhoneNotifier 电话通知(模拟,仅 P0)
type PhoneNotifier struct {
	PhoneNumbers []string
}

func (n *PhoneNotifier) Name() string { return "phone" }

func (n *PhoneNotifier) Send(alert *Alert) error {
	if alert.Level > AlertP0 {
		return nil // 只有 P0 才打电话
	}
	fmt.Printf("  📞 拨打电话通知 %v: %s\n", n.PhoneNumbers, alert.Title)
	time.Sleep(50 * time.Millisecond)
	return nil
}

// ============================================================
// 6. 告警风暴抑制
// ============================================================

type StormSuppressor struct {
	mu          sync.Mutex
	alertCount  map[string]int64 // fingerprint -> count in window
	windowStart time.Time
	windowSize  time.Duration
	maxPerWindow int64
	dropped      int64
}

func NewStormSuppressor(windowSize time.Duration, maxPerWindow int64) *StormSuppressor {
	return &StormSuppressor{
		alertCount:   make(map[string]int64),
		windowStart:  time.Now(),
		windowSize:   windowSize,
		maxPerWindow: maxPerWindow,
	}
}

// ShouldSuppress 判断是否应该抑制(防止告警风暴)
func (s *StormSuppressor) ShouldSuppress(fingerprint string) bool {
	s.mu.Lock()
	defer s.mu.Unlock()

	// 重置窗口
	if time.Since(s.windowStart) > s.windowSize {
		s.alertCount = make(map[string]int64)
		s.windowStart = time.Now()
	}

	s.alertCount[fingerprint]++
	total := int64(0)
	for _, count := range s.alertCount {
		total += count
	}

	if total > s.maxPerWindow {
		s.dropped++
		return true
	}
	return false
}

// ============================================================
// 7. 辅助函数
// ============================================================

func metricKey(name string, labels map[string]string) string {
	return fmt.Sprintf("%s{%s}", name, labelsToString(labels))
}

func labelsToString(labels map[string]string) string {
	keys := make([]string, 0, len(labels))
	for k := range labels {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	
	parts := make([]string, len(keys))
	for i, k := range keys {
		parts[i] = fmt.Sprintf("%s=%s", k, labels[k])
	}
	return strings.Join(parts, ",")
}

func matchesLabels(labels map[string]string, match map[string]string) bool {
	for k, v := range match {
		if labels[k] != v {
			return false
		}
	}
	return true
}

func atoi(s string) int {
	n := 0
	for _, c := range s {
		if c >= '0' && c <= '9' {
			n = n*10 + int(c-'0')
		}
	}
	return n
}

// ============================================================
// 8. 主程序演示
// ============================================================

func main() {
	fmt.Println("========== 第7讲:实时告警与自动化响应 ==========\n")

	// 初始化组件
	ruleEngine := NewRuleEngine()
	stormSuppressor := NewStormSuppressor(1*time.Minute, 50)
	autoResponder := NewAutoResponder()

	alertManager := &AlertManager{
		alerts:       make(map[string]*Alert),
		firingAlerts: make(map[string]*Alert),
		rules:        ruleEngine,
		notifiers: []Notifier{
			&ConsoleNotifier{},
			&DingTalkNotifier{WebhookURL: "https://oapi.dingtalk.com/robot/send"},
			&PhoneNotifier{PhoneNumbers: []string{"13800138000"}},
		},
	}

	// ============================================================
	// Part 1: 注册告警规则
	// ============================================================
	fmt.Println("--- Part 1: 注册告警规则 ---")

	// 规则1:CPU 过高
	ruleEngine.RegisterRule(&AlertRule{
		ID:          "cpu-high",
		Name:        "CPU 使用率过高",
		Description: "CPU 使用率超过 90% 持续 5 分钟",
		Level:       AlertP1,
		Enabled:     true,
		MetricName:  "cpu_usage",
		Operator:    ">",
		Threshold:   90.0,
		Duration:    5 * time.Minute,
		Labels:      map[string]string{"service": "ai-app"},
		Annotations: map[string]string{"summary": "CPU 使用率过高,建议扩容"},
		AutoActions: []AutoActionDef{
			{
				ActionType: "scale_up",
				Target:     "ai-app",
				Cooldown:   10 * time.Minute,
				Params:     map[string]interface{}{"replicas": 2},
			},
		},
	})

	// 规则2:错误率飙升
	ruleEngine.RegisterRule(&AlertRule{
		ID:          "error-rate-spike",
		Name:        "错误率飙升",
		Description: "HTTP 500 错误率超过 5%",
		Level:       AlertP0,
		Enabled:     true,
		MetricName:  "error_rate",
		Operator:    ">",
		Threshold:   5.0,
		Duration:    1 * time.Minute,
		Labels:      map[string]string{"service": "ai-app"},
		Annotations: map[string]string{"summary": "错误率超过 5%,立即排查"},
		AutoActions: []AutoActionDef{
			{
				ActionType: "circuit_breaker",
				Target:     "ai-app",
				Cooldown:   5 * time.Minute,
				Params:     map[string]interface{}{"duration": 30},
			},
		},
	})

	// 规则3:QPS 突降
	ruleEngine.RegisterRule(&AlertRule{
		ID:          "qps-drop",
		Name:        "QPS 突降",
		Description: "QPS 相比前一分钟下降超过 50%",
		Level:       AlertP1,
		Enabled:     true,
		MetricName:  "qps",
		Operator:    "<",
		Threshold:   50.0,
		Duration:    0,
		Labels:      map[string]string{"service": "ai-app"},
		Annotations: map[string]string{"summary": "QPS 突降,可能服务不可用"},
		AutoActions: []AutoActionDef{
			{
				ActionType: "restart",
				Target:     "ai-app",
				Cooldown:   3 * time.Minute,
			},
		},
	})

	// 规则4:LLM 延迟过高
	ruleEngine.RegisterRule(&AlertRule{
		ID:          "llm-latency",
		Name:        "LLM 响应延迟过高",
		Description: "LLM 平均响应时间超过 5 秒",
		Level:       AlertP2,
		Enabled:     true,
		MetricName:  "llm_latency_ms",
		Operator:    ">",
		Threshold:   5000.0,
		Duration:    3 * time.Minute,
		Labels:      map[string]string{"service": "ai-app", "model": "gpt-4o-mini"},
		Annotations: map[string]string{"summary": "LLM 响应慢,考虑降级到更快的模型"},
		SilencePeriods: []SilencePeriod{
			{
				Start:    "02:00",
				End:      "06:00",
				Weekdays: []time.Weekday{time.Saturday, time.Sunday},
				Reason:   "周末低峰期维护窗口",
			},
		},
	})

	// 规则5:复合规则:错误率高 + QPS 下降
	ruleEngine.RegisterRule(&AlertRule{
		ID:          "error-and-drop",
		Name:        "错误率高且流量下降",
		Description: "错误率 > 3% 且 QPS < 100",
		Level:       AlertP0,
		Enabled:     true,
		Conditions: []RuleCondition{
			{MetricName: "error_rate", Operator: ">", Threshold: 3.0},
			{MetricName: "qps", Operator: "<", Threshold: 100.0},
		},
		Labels:      map[string]string{"service": "ai-app"},
		Annotations: map[string]string{"summary": "严重故障,立即响应"},
		AutoActions: []AutoActionDef{
			{
				ActionType: "rollback",
				Target:     "ai-app",
				Cooldown:   15 * time.Minute,
			},
		},
	})

	// ============================================================
	// Part 2: 模拟指标上报和告警触发
	// ============================================================
	fmt.Println("\n--- Part 2: 模拟指标上报和告警触发 ---")

	simulateMetrics := []MetricSample{
		// 正常情况
		{Name: "cpu_usage", Value: 45.0, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app"}},
		{Name: "error_rate", Value: 0.5, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app"}},
		{Name: "qps", Value: 800.0, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app"}},
		{Name: "llm_latency_ms", Value: 320.0, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app", "model": "gpt-4o-mini"}},
		
		// CPU 飙高
		{Name: "cpu_usage", Value: 82.0, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app"}},
		{Name: "cpu_usage", Value: 94.0, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app"}}, // 触发 CPU 告警
		
		// 错误率飙升
		{Name: "error_rate", Value: 3.2, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app"}},
		{Name: "error_rate", Value: 7.8, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app"}}, // 触发 P0 告警
		
		// QPS 突降
		{Name: "qps", Value: 450.0, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app"}},
		{Name: "qps", Value: 185.0, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app"}},
		
		// LLM 延迟
		{Name: "llm_latency_ms", Value: 2800.0, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app", "model": "gpt-4o-mini"}},
		{Name: "llm_latency_ms", Value: 6200.0, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app", "model": "gpt-4o-mini"}}, // 触发延迟告警
		
		// 复合条件:错误率高 + QPS 低
		{Name: "error_rate", Value: 5.5, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app"}},
		{Name: "qps", Value: 65.0, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app"}}, // 触发复合告警
	}

	for _, sample := range simulateMetrics {
		alerts := ruleEngine.Evaluate(&sample)
		
		for _, alert := range alerts {
			// 风暴抑制检查
			if stormSuppressor.ShouldSuppress(alert.Fingerprint) {
				fmt.Printf("  ⛈️ [风暴抑制] 丢弃告警: %s\n", alert.Title)
				continue
			}

			alertManager.ProcessAlerts([]*Alert{alert})

			// 执行自动化响应
			rule, _ := ruleEngine.rules[alert.Labels["rule_id"]]
			if rule != nil {
				for _, actionDef := range rule.AutoActions {
					autoResponder.Execute(alert, actionDef)
				}
			}
		}
	}

	// ============================================================
	// Part 3: 告警确认与解决
	// ============================================================
	fmt.Println("\n--- Part 3: 告警确认与解决 ---")

	// 列出所有活跃告警
	fmt.Println("\n当前活跃告警:")
	for _, alert := range alertManager.alerts {
		if alert.Status == AlertStatusFiring {
			fmt.Printf("  %s [%s] %s\n", alert.Level, alert.ID[:8], alert.Title)
		}
	}

	// 确认一个告警
	for _, alert := range alertManager.alerts {
		if alert.Status == AlertStatusFiring {
			alertManager.Acknowledge(alert.ID, "ops-user")
			break
		}
	}

	// 解决一个告警
	for _, alert := range alertManager.alerts {
		if alert.Status == AlertStatusAcknowledged {
			alertManager.Resolve(alert.ID)
			break
		}
	}

	// ============================================================
	// Part 4: 告警统计
	// ============================================================
	fmt.Println("\n--- Part 4: 告警统计 ---")

	levelCount := map[AlertLevel]int64{}
	for _, alert := range alertManager.alerts {
		levelCount[alert.Level]++
	}

	fmt.Println("告警级别分布:")
	for level := AlertP0; level <= AlertP3; level++ {
		fmt.Printf("  %s: %d\n", level, levelCount[level])
	}

	fmt.Printf("\n自动化响应执行统计:\n")
	successCount := 0
	failCount := 0
	for _, result := range autoResponder.actionLog {
		if result.Success {
			successCount++
		} else {
			failCount++
		}
		fmt.Printf("  %s → %s: %v (%s)\n", 
			result.Action.ActionType, result.Action.Target, result.Success, result.Output)
	}
	fmt.Printf("  成功率: %.1f%%\n", float64(successCount)/float64(successCount+failCount)*100)

	fmt.Printf("\n风暴抑制统计:\n")
	fmt.Printf("  丢弃告警数: %d\n", stormSuppressor.dropped)

	// ============================================================
	// 总结
	// ============================================================
	fmt.Println("\n" + strings.Repeat("=", 53))
	fmt.Println("实时告警与自动化响应最佳实践总结")
	fmt.Println(strings.Repeat("=", 53))
	fmt.Println(`
1. 分级告警
   P0 🔴 致命:立即处理,电话通知
   P1 🟠 严重:5分钟内处理
   P2 🟡 警告:30分钟内处理
   P3 🔵 通知:下一个工作日处理

2. 规则引擎
   - 简单阈值:CPU > 90%
   - 趋势检测:错误率持续上升
   - 同比对比:相比昨天同时间增长 50%
   - 复合条件:错误率高 + QPS 下降

3. 自动化响应
   - 自动扩容:CPU 高了加机器
   - 熔断保护:错误率高了切断流量
   - 自动回滚:新版本有问题立刻回退
   - 限流降级:保护下游不被压垮

4. 风暴抑制
   - 去重:相同指纹合并
   - 抑制:已知故障不重复告警
   - 限速:单位时间内最多 N 条
   - 静默:维护窗口不告警

5. 告警质量
   - 每个告警必须有明确的修复步骤
   - 避免"垃圾告警":无法行动的告警不如不告
   - 告警要附带上下文:哪个服务、哪个接口、哪个用户
   - 定期清理无效告警规则`)
}

var _ = json.Marshal

四、运行示例输出(续)

复制代码
当前活跃告警:
  P0 🔴 [alert-a1b2] 错误率飙升
  P1 🟠 [alert-c3d4] CPU 使用率过高
  P2 🟡 [alert-e5f6] LLM 响应延迟过高
  P0 🔴 [alert-g7h8] 错误率高且流量下降

[AlertManager] ops-user 确认告警: 错误率飙升
[AlertManager] 告警已解决: 错误率飙升 (持续 34.567ms)

--- Part 4: 告警统计 ---

告警级别分布:
  P0 🔴: 2
  P1 🟠: 1
  P2 🟡: 1
  P3 🔵: 0

自动化响应执行统计:
  scale_up → ai-app: true (ai-app 已扩容到 2 副本)
  circuit_breaker → ai-app: true (ai-app 已熔断 30s)
  rollback → ai-app: true (ai-app 已回滚到上一版本)
  成功率: 100.0%

风暴抑制统计:
  丢弃告警数: 17

==========
实时告警与自动化响应最佳实践总结
==========

1. 分级告警
   P0 🔴 致命:立即处理,电话通知
   P1 🟠 严重:5分钟内处理
   P2 🟡 警告:30分钟内处理
   P3 🔵 通知:下一个工作日处理

2. 规则引擎
   - 简单阈值:CPU > 90%
   - 趋势检测:错误率持续上升
   - 同比对比:相比昨天同时间增长 50%
   - 复合条件:错误率高 + QPS 下降

3. 自动化响应
   - 自动扩容:CPU 高了加机器
   - 熔断保护:错误率高了切断流量
   - 自动回滚:新版本有问题立刻回退
   - 限流降级:保护下游不被压垮

4. 风暴抑制
   - 去重:相同指纹合并
   - 抑制:已知故障不重复告警
   - 限速:单位时间内最多 N 条
   - 静默:维护窗口不告警

5. 告警质量
   - 每个告警必须有明确的修复步骤
   - 避免"垃圾告警":无法行动的告警不如不告
   - 告警要附带上下文:哪个服务、哪个接口、哪个用户
   - 定期清理无效告警规则

五、告警规则配置示例(YAML)

复制代码
# alert-rules.yaml
groups:
  - name: ai-app-critical
    interval: 30s
    
    rules:
      # ============================================
      # P0 规则:致命告警,电话通知
      # ============================================
      
      - alert: HighErrorRate
        expr: error_rate{service="ai-app"} > 5.0
        for: 1m
        labels:
          severity: critical
          priority: P0
        annotations:
          summary: "错误率超过 5%"
          description: "{{ $labels.service }} 错误率 {{ $value }}% 已持续 1 分钟"
          action: "1. 检查最近部署;2. 回滚到上一版本;3. 通知值班人员"
          
      - alert: ServiceDown
        expr: up{service="ai-app"} == 0
        for: 10s
        labels:
          severity: critical
          priority: P0
        annotations:
          summary: "服务宕机"
          description: "{{ $labels.instance }} 已不可达"
          action: "1. 检查机器状态;2. 尝试重启;3. 拉起备用实例"
          
      # ============================================
      # P1 规则:严重告警,5分钟内处理
      # ============================================
      
      - alert: HighCPU
        expr: cpu_usage{service="ai-app"} > 85
        for: 5m
        labels:
          severity: warning
          priority: P1
        annotations:
          summary: "CPU 使用率过高"
          description: "{{ $labels.service }} CPU 使用率 {{ $value }}%"
          action: "1. 检查是否存在异常进程;2. 考虑扩容;3. 分析慢查询"
          
      - alert: QPSDrop
        expr: qps{service="ai-app"} < 100
        for: 2m
        labels:
          severity: warning
          priority: P1
        annotations:
          summary: "QPS 异常下降"
          description: "当前 QPS {{ $value }},低于阈值 100"
          action: "1. 检查上游依赖;2. 检查 DNS/负载均衡;3. 查看入口流量"
          
      # ============================================
      # P2 规则:警告告警,30分钟内处理
      # ============================================
      
      - alert: HighLatency
        expr: llm_latency_ms{model="gpt-4o-mini"} > 3000
        for: 3m
        labels:
          severity: info
          priority: P2
        annotations:
          summary: "LLM 响应延迟偏高"
          description: "模型 {{ $labels.model }} 平均延迟 {{ $value }}ms"
          action: "1. 检查模型 API 状态;2. 考虑切换到备用模型;3. 检查网络延迟"
          
      - alert: MemoryPressure
        expr: memory_usage{service="ai-app"} > 80
        for: 10m
        labels:
          severity: info
          priority: P2
        annotations:
          summary: "内存使用率过高"
          description: "{{ $labels.service }} 内存使用率 {{ $value }}%"
          action: "1. 检查内存泄漏;2. 调整 JVM/Go GC 参数;3. 计划扩容"
          
      # ============================================
      # P3 规则:通知,下个工作日处理
      # ============================================
      
      - alert: DiskUsage
        expr: disk_usage{device="/data"} > 70
        for: 24h
        labels:
          severity: info
          priority: P3
        annotations:
          summary: "磁盘使用率持续高位"
          description: "{{ $labels.device }} 使用率 {{ $value }}% 已持续 24 小时"
          action: "1. 清理过期日志;2. 检查是否需要扩容磁盘;3. 归档冷数据"
          
      - alert: CertificateExpiry
        expr: cert_expiry_days < 30
        for: 0
        labels:
          severity: info
          priority: P3
        annotations:
          summary: "SSL 证书即将过期"
          description: "证书 {{ $labels.domain }} 还有 {{ $value }} 天过期"
          action: "1. 申请新证书;2. 更新 LB 配置;3. 验证新证书生效"

六、自动化响应策略矩阵

场景 检测条件 自动动作 冷却期 升级条件
CPU 飙高​ CPU > 85% 持续 5min 自动扩容 +2 副本 10min 扩容后仍 > 90% → P0
错误率飙升​ 5xx > 5% 持续 1min 熔断 30s + 回滚 5min 熔断后仍 > 10% → 电话
内存泄漏​ 内存持续增长 4h 重启服务 30min 每天重启超过 3 次 → P0
慢查询​ P99 > 5s 持续 10min 切换只读副本 15min 所有副本都慢 → 降级
依赖故障​ 外部 API 超时率 > 20% 开启缓存 + 降级 5min 缓存命中率 < 50% → P0
磁盘满​ 磁盘 > 85% 清理 7 天前日志 1h 清理后仍 > 90% → P1
证书过期​ 剩余 < 30 天 自动续签 (Let's Encrypt) 24h 续签失败 → 人工介入

七、生产部署建议

7.1 告警路由配置

复制代码
# alertmanager.yml
route:
  receiver: 'default'
  group_by: ['alertname', 'cluster']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  
  routes:
    - match:
        severity: critical
      receiver: 'p0-oncall'
      repeat_interval: 5m
      
    - match:
        severity: warning
      receiver: 'p1-group'
      
    - match:
        severity: info
      receiver: 'daily-report'

receivers:
  - name: 'p0-oncall'
    pagerduty_configs:
      - routing_key: 'xxx'
        severity: critical
        
  - name: 'p1-group'
    wechat_configs:
      - webhook_url: 'https://qyapi.weixin.qq.com/xxx'
        send_resolved: true
        
  - name: 'daily-report'
    email_configs:
      - to: 'team@example.com'

7.2 告警疲劳度管理

复制代码
# 告警疲劳度配置
fatigue:
  # 同一个告警 1 小时内最多触发 3 次
  per_alert:
    max_count: 3
    window: 1h
    action: silence
    
  # 同一个服务 10 分钟内最多 10 条告警
  per_service:
    max_count: 10
    window: 10m
    action: aggregate
    
  # 全局 1 分钟内最多 100 条
  global:
    max_count: 100
    window: 1m
    action: throttle

7.3 告警升级机制

复制代码
escalation:
  - alert_level: P0
    timeline:
      - 0m:    通知值班工程师
      - 5m:    未确认 → 通知技术主管
      - 15m:   未解决 → 通知 CTO
      - 30m:   未解决 → 启动 War Room
      
  - alert_level: P1
    timeline:
      - 0m:    通知值班工程师
      - 15m:   未确认 → 通知技术主管
      - 60m:   未解决 → 升级为 P0

八、关键要点

  1. 告警不是越多越好 --- 每天 1000 条告警 = 0 条有效告警,人都麻了
  2. 自动化是王道 --- 80% 的故障可以用脚本自动恢复,别让人半夜爬起来重启
  3. 风暴抑制保命 --- 没有风暴抑制,告警系统第一个死
  4. 上下文比数值重要 --- 告诉人怎么修,比告诉他出问题了更有用
  5. 冷却期防抖 --- 别让同一个问题反复触发告警
  6. 升级机制兜底 --- 人总会睡着,要有自动升级机制
  7. 定期演练 --- 每个月搞一次 Chaos Engineering,看看告警好不好使

🧰 开发之余的小工具推荐

配置告警规则时经常需要测试正则表达式和 YAML 格式是否正确。zz365.top 的正则测试工具可以实时匹配和调试,YAML 格式化工具能帮你快速检查缩进和语法错误。所有工具纯前端运行,你的敏感配置数据不会上传到服务器。


**下一讲预告:**​ 第8讲「分布式链路追踪与根因分析」------ 采样策略、Span 传播、服务拓扑、火焰图、根因定位算法。

相关推荐
Gopher_HBo1 小时前
zap WriteSyncer与Sink体系
后端
小小龙学IT1 小时前
Go 语言 database/sql 标准库深度解析:从连接池到驱动抽象
数据库·sql·golang
合尘猫1 小时前
Nginx stream 做 GitHub 443 SNI 透传:完整配置、多上游故障转移与三个坑
后端·程序员·开源
知守观1 小时前
百万级数据导出OOM:POI的坑与EasyExcel的流式写入实战(附内存对比)
java·后端
“AI国潮设计-小江”1 小时前
Python实战 | SDXL批量生成“潮汕英歌舞”国潮甜品IP,附核心Prompt与商用授权思路
开发语言·人工智能·python·prompt·aigc
H.莓飛1 小时前
【数据结构】栈
linux·开发语言·数据结构·算法·centos
无糖可乐没有灵魂1 小时前
用一篇文章来精通“进程/线程/协程/锁“多并发任务
开发语言·php
2601_962885721 小时前
如何用 Python 分析股票的日历效应?(星期效应与月份效应,附数据挖掘陷阱)
开发语言·python·数据挖掘
蜗牛互联网1 小时前
Computer Use公共预览的安全设计:应用、动作与数据三维门禁
java·人工智能·后端·安全·策略模式