可观测性之饱和度

Posted on Thu 15 May 2025 in tech

Abstract 可观测性之饱和度
Authors Walter Fan
 Category    learning note  
Status v1.0
Updated 2025-05-15
License CC-BY-NC-ND 4.0

🚥 可观测性之饱和度

一、什么是可观测性(Observability)?

在现代软件系统中,可观测性指的是:通过收集系统的外部输出(如日志、指标、追踪)来推断系统内部状态的能力。它不仅是监控的一部分,更是一种设计理念,目标是帮助开发与运维团队快速发现、定位和解决问题

可观测性强的系统,可以回答这样的问题:

  • 系统为什么变慢了?
  • 某个请求为什么失败?
  • 服务是否濒临崩溃?

二、四大黄金指标(Four Golden Signals)

Google SRE 团队在《Site Reliability Engineering》一书中提出了四个关键的可观测性指标,即“四大黄金信号”(Four Golden Signals):

指标 含义 举例
Latency(延迟) 请求完成所需时间 登录接口平均响应时间 500ms
Traffic(流量/使用量) 系统处理的请求量或数据量 每秒接收 1000 个 API 请求
Errors(错误) 请求失败或不符合预期的比例 5% 的支付请求返回 500 错误
Saturation(饱和度) 系统资源是否接近瓶颈 数据库连接池使用率 99%

前三个指标关注的是系统外部的表现,而饱和度则更像是一种内部健康信号,是预警的关键。


三、深入理解饱和度(Saturation)

什么是饱和度?

饱和度指的是一个系统在资源使用上接近极限的程度,是衡量系统“快撑不住”的程度。

当 CPU、内存、线程、连接数等资源接近用完时,系统虽然可能还在正常运行,但已经进入危险区——稍微再增加一点负载,就会出现排队、超时、错误等问题。


饱和度 ≠ 错误,但是错误的前奏

举个比喻:

饱和度就像一个高速公路已经塞满了车,但还没真正堵死。如果继续增加车辆,就会交通瘫痪。


四、饱和度的常见表现形式与实例

下面我们通过几个典型资源维度,说明饱和度如何表现,以及如何监控。


1. CPU 饱和

  • 症状:CPU 使用率长期超过 85%,服务响应变慢
  • 实例:Go HTTP 服务处理图像上传,上传量大增,CPU 一直跑在 95%,导致响应时间从 200ms 增加到 1s+
  • 指标node_cpu_seconds_totalprocess_cpu_seconds_total

2. 内存饱和

  • 症状:内存使用接近上限,频繁触发 GC,出现长时间暂停
  • 实例:Java 服务处理大批量 JSON 请求,内存使用达 98%,频繁 Full GC,响应时间不稳定
  • 指标jvm_memory_used_bytesgo_memstats_heap_alloc_bytes、GC 停顿时间

3. 连接池饱和

  • 症状:数据库/Redis 连接池满,新的请求被阻塞或拒绝
  • 实例:服务高峰期数据库连接池(最大 100)全部被占用,新的请求提示 connection timeout
  • 指标db_pool_active_connectionsdb_pool_waiting_connections

4. 线程/协程池饱和

  • 症状:Web 服务线程池满,新的请求排队甚至被拒绝
  • 实例:Spring Boot 应用线程池最大 200,活跃线程数长期维持在 200,出现 RejectedExecutionException
  • 指标active_threads / max_threadsgo_goroutinesjvm_threads_live

5. 消息/请求队列堆积

  • 症状:任务处理速度慢于接收速度,队列堆积
  • 实例:RabbitMQ 中订单处理队列从 100 增长到 50,000,消费者压力巨大
  • 指标queue_lengthpending_tasks

五、饱和度指标的监控与告警建议

要让饱和度发挥作用,建议为以下场景设置阈值告警:

CPU 使用率 > 90% 且持续 5 分钟
数据库连接池使用率 > 95%
线程池活跃线程数 = 最大线程数
请求队列长度 > 1000
GC 时间 > 1 秒 且频率异常

这些告警可以帮你在系统彻底崩溃前就采取措施,如:

  • 扩容实例
  • 实施限流
  • 降级非核心功能
  • 优化资源使用

六、实例

以一个典型的 Go 服务为例, 假设具备以下特性:

  • 使用 net/httpGin/Echo 提供 HTTP API
  • 有业务逻辑处理、数据库访问、任务队列等操作
  • 已使用 Prometheus + Grafana 进行监控
  • 服务部署在 Linux 上(如 K8s 或裸机)

1. 饱和度监控的关键维度

资源维度 风险表现 饱和度指标示例
CPU 使用率 高负载下处理变慢 process_cpu_seconds_totalnode_cpu_seconds_total
内存使用率 GC 频繁、服务变卡、OOM 崩溃 go_memstats_alloc_bytesgo_memstats_heap_inuse_bytes
Goroutine 数 协程泄漏或堆积,服务阻塞 go_goroutines
线程数 操作系统资源瓶颈 go_threads(如启用)或 ps 工具
数据库连接池 连接耗尽、排队 自定义指标,如 db_pool_in_use / db_pool_max
HTTP 请求排队 请求未能及时处理、响应变慢 中间件记录队列时间、自定义指标
任务队列长度 队列堆积、处理线程/协程压力大 queue_lengthpending_tasks(需自定义)

2.Prometheus 具体监控指标配置

2.1 CPU 使用率

100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[1m])) * 100)
  • 告警建议:CPU 使用率 > 90%,持续 3 分钟
  • 可视化:Grafana 曲线图,颜色随负载变化

2.2 内存使用 / GC 时间

go_memstats_alloc_bytes / go_memstats_sys_bytes
rate(go_gc_duration_seconds_sum[5m])
  • 告警建议:

  • 内存使用率 > 90%

  • GC 时间 > 500ms 且频率 > 每分钟 5 次

2.3 Goroutine 数量

go_goroutines
  • 告警建议:

  • goroutine 数量 > 平均值 2 倍,持续 3 分钟

  • goroutine 数呈线性增长趋势(泄漏可能)

2.4 数据库连接池使用率(需你在代码中暴露)

假设你用的是 GORM + PostgreSQL,建议暴露如下自定义指标:

db_pool_in_use / db_pool_max

示例注册 Prometheus 指标:

prometheus.NewGaugeFunc(prometheus.GaugeOpts{
    Name: "db_pool_in_use_ratio",
    Help: "Database connection pool usage ratio",
}, func() float64 {
    stats := db.DB().Stats()
    return float64(stats.InUse) / float64(stats.MaxOpenConnections)
})
  • 告警建议:使用率 > 95%,持续 2 分钟

2.5 HTTP 请求排队时间(需自定义中间件)

在中间件中记录请求开始到真正处理的延迟:

func queueTimeMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        queuedAt := time.Now()
        next.ServeHTTP(w, r)
        queueDuration := time.Since(queuedAt)
        queueTimeHistogram.Observe(queueDuration.Seconds())
    })
}

注册 Prometheus Histogram:

queueTimeHistogram = prometheus.NewHistogram(prometheus.HistogramOpts{
    Name:    "http_request_queue_duration_seconds",
    Help:    "Time spent in request queue before processing",
    Buckets: prometheus.DefBuckets,
})
  • 告警建议:P95 请求队列时间 > 100ms

2.6 任务队列长度(如使用 channel、Redis、Kafka)

如果你有一个内部任务队列,可自定义指标:

taskQueueLength = prometheus.NewGauge(prometheus.GaugeOpts{
    Name: "internal_task_queue_length",
    Help: "Current length of internal async task queue",
})

在生产/消费中更新它:

taskQueueLength.Set(float64(len(taskChan)))
  • 告警建议:队列长度 > 平均值 3 倍 或 超过安全阈值(如 1000)

3. Grafana 面板建议

建议创建一个 Saturation Dashboard,包含:

面板 图表类型 描述
CPU 使用率 折线图 节点整体 CPU
内存使用 & GC 时间 折线图 + 柱状图 查看 GC 高峰
goroutines 折线图 goroutine 增长趋势
DB 连接池使用率 热力图 查看使用高峰
请求队列时间 分位直方图 重点看 P95
队列长度 折线图 消费速度 vs 队列增长

4. 预防机制建议

结合饱和度指标,可以实施以下策略:

策略 应对场景
限流(如令牌桶) 请求过多,goroutine 飙升
自动扩容(HPA) CPU/Goroutine 饱和时动态增实例
资源隔离 后台任务、数据库查询用不同线程池
降级 请求过载时跳过缓存、关闭非核心功能
指标驱动告警 结合饱和度触发 PagerDuty / 企业微信通知

七、源码

https://github.com/walterfan/kata-go/tree/master/kata/prompt_service


功能模块

模块 功能说明
main.go 程序入口,使用 cobra 支持命令行参数(如监听端口),集成 zap 日志系统。
pkg/database/sqlite.go 初始化 SQLite 数据库连接,并提供数据迁移和初始化样本数据的功能。
pkg/models/prompt.go 定义 Prompt 结构体,映射数据库表结构,包含字段如 Name, Description, Tags, UserPrompt, SystemPrompt 等。
pkg/handlers/prompt_handler.go 提供 RESTful API 接口:
- GET /metrics: 获取 Prometheus 监控指标
- POST /api/v1/prompts: 创建 Prompt
- GET /api/v1/prompts/:id: 获取单个 Prompt
- PUT /api/v1/prompts/:id: 更新 Prompt
- DELETE /api/v1/prompts/:id: 删除 Prompt
- GET /api/v1/prompts: 支持关键字搜索与分页
pkg/metrics/metrics.go 集成 Prometheus 指标监控,记录 HTTP 请求次数、耗时等信息。

技术栈

技术 用途
Gin Web 框架,用于构建 HTTP 服务。
GORM + SQLite ORM 和数据库,用于持久化存储 prompts 数据。
Prometheus + Metrics Middleware 监控接口调用次数、延迟等运行指标。
Zap 高性能日志库,用于记录服务日志。
Cobra CLI 命令行支持,用于解析启动参数(如监听端口)。

启动服务 go run main.go -p 8888 调用 curl http://localhost:8888/metrics 即可观察服务运行的度量指标。


八、小结

在实际工作中,我们常常关注“慢没慢”、“错没错”,但忽略了“快顶不住了”这个临界状态。饱和度正是你发现“即将撑不住”时的哨兵。

饱和度 ≠ 故障,但它预示着故障即将发生。

一个具备良好可观测性的系统,应该同时涵盖四大黄金指标。尤其是高并发、微服务、分布式架构中,饱和度监控是不可或缺的。 它是提前预警系统压力的重要信号,监控饱和度能让你:

  • 在服务抖动前就开始扩容或限流
  • 更有效地定位是 CPU、内存、线程还是连接池成为瓶颈
  • 做好弹性架构与可用性保障

本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。