可观测性

可观测性(Observability) 指通过系统外部输出推断其内部状态的能力。
维度(Dimension) 是用来描述和分类数据的标签属性,比如用户ID、应用ID、模型名称等,关注“是什么”
指标(Metric) 是用来量化的数值数据,比如请求次数、响应时间、Token消耗量等,关注“有多少”

监控等数据分类

系统指标:包括cpu使用率、内存占用、磁盘I/O、网络流量等基础设施层面的监控数据。
应用指标:涵盖接口响应时间、QPS(每秒查询率)、错误率】jvm状态等应用层面的性能数据。
业务指标:针对我们平台的特定业务逻辑,比如AI模型调用次数、token消耗量、用户活跃度等
调用链:在一个分布式系统中,一个请求可能经过多个服务组件。
百分位数:P99:99%的请求响应时间都在这个值以下。

怎么实现

1.统计什么
需要根据业务特点确定关键指标,既要覆盖系统层面的通用指标,也要包含业务特有的监控维度
2.如何收集
数据收集是可观测性的基础,可以通过代码埋点、探针技术、日志分析等多种方式实现
3.如何存储
监控数据通常量大且连续,需要选择合适的存储方案,比如时序数据库或专门的监控系统
4.如何展示
数据面板,实时监控

ARMS系统监控

阿里云提供的应用实时监控服务,采用了探针技术,能够在不修改应用代码的情况下,自动收集和分析应用性能数据,快速构建实时的监控能力。

监控指标设计
首先我们需要明确监控哪些维度和业务指标
维度:
用户ID 应用ID 模型名称 最大输出Token数 AI回复消息内容 模型生成停止原因 调用状态 请求时间 调用失败的错误信息

指标:
token 消耗 响应时长
分析能力
有了基础数据我们可以进行很多分析
模型调用分析:统计不同时间窗口(分钟/小时/天)内,每个模型、用户、应用的调用次数趋势
模型性能分析:分析各模型的平均响应时间,以及响应时间分布的P50/P90百分位数
、、、
数据收集方式
在业务监控中,数据收集需要开发者手动埋点,因为只有开发者才知道要收集什么信息、从哪里收集、什么时候收集。外面的策略是在业务层收集最原始、最细粒度的数据。

数据存储方案-prometheus
prometheus是一个开源的监控系统,专门为时序数据的收集、存储和查询而设计

我们通过jobs和instances配置需要拉取的数据任务和服务实例

  • job(任务):代表「一类被采集的服务」,通常对应一个应用或组件,比如 kiosk-backend、chat-backend、mysql。在 Prometheus 配置里每写一条 job_name: xxx,就是告诉它「这一类目标是一组服务,逻辑上算一类」。
  • instance(实例):代表「这个 job 下面的某一台/某个端口的具体实例」,由 targets 决定,比如 10.0.0.1:8000、10.0.0.2:8000,Prometheus 会自动给每条时间序列打上 instance=”10.0.0.1:8000” 这样的标签。
    组合关系:同一个 job 下可以有很多 instance,用 job 看“这是什么服务/应用”,用 instance 看“这个服务的每个副本/节点各自怎么样”(整体 vs. 单机对比)。

指标类型
counter:累积计数器,只能增加或者重置为零,适合统计请求总数、错误次数等单调递增的指标。
gauge:仪表盘类型,数值可以任意上下波动,适合记录当前状态值,比如内存使用量、当前在线用户数、队列长度等。
histogram:直方图类型,用于观察数据分布情况,比如请求响应时间等分布。
summary:和Histogram类似,但它在客户端预先计算百分位数,沭河需要精确百分位数计算但对网络传输有要求的场景。

存储机制
为了更快查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
flowchart TD
A([数据采集 Scrape]) --> B{写入层}

subgraph In_Memory [内存缓冲区]
B --> C[Memory Block]
B --> D[WAL 预写日志]
end

D -- 故障恢复 --> C
C -- 每2小时周期 --> E[2h Block 持久化]

subgraph Disk_Storage [磁盘持久化层]
E --> F[Index / Chunks / Meta]
F --> G[Background Compaction]
G --> H[(长期存储块)]
end

style D fill:#fff2cc,stroke:#d6b656
style H fill:#dae8fc,stroke:#6c8ebf

说明:

  • 数据采集先写入当前活动内存块(Memory Block)和 WAL 日志
  • 内存块每 2 小时通过 Block Compaction 转为磁盘持久化块
  • 持久化块后台压缩,部分数据进入长期存储
  • 如系统崩溃,WAL Replay 可恢复数据


名词解释:

redis/jvm/full gc/连接池/探针技术/TSDB/k8s

Redis

Redis(Remote Dictionary Server)是一种内存型键值数据库,常用于缓存、会话存储、消息队列等。数据主要在内存中,读写很快,支持多种数据结构(字符串、哈希、列表、集合、有序集合等)。在监控场景里常用来做指标缓存、限流、分布式锁等。


JVM

JVM(Java Virtual Machine,Java 虚拟机)是运行 Java 字节码的运行时环境。Java 程序编译成 .class 字节码后,由 JVM 解释或即时编译(JIT)成机器码执行。JVM 负责内存管理(堆、栈、方法区等)和垃圾回收(GC),所以谈 Java 性能、GC、内存问题时都会涉及 JVM。


Full GC

Full GC(Full Garbage Collection,完全垃圾回收)是 JVM 对整个堆做的一次垃圾回收,包括年轻代(Young Generation)和老年代(Old Generation)。Full GC 会暂停应用线程(Stop-The-World),耗时相对较长,频繁 Full GC 会导致延迟抖动和吞吐下降,是性能调优时重点关注的指标。


连接池(Connection Pool)

连接池是一组预先创建并复用的网络/数据库连接。应用从池中取连接用完后归还,而不是每次请求都新建、关闭连接,从而减少建连/断连的开销和资源占用。常见于数据库(如 JDBC 连接池)、Redis、HTTP 客户端等。监控里常会看连接池使用率、等待数、超时等。


探针技术(Probe / Agent)

探针(或 Agent)是在应用进程内或主机上部署的采集程序,用来无侵入或低侵入地采集指标、日志、链路等数据。例如:

  • JVMTI / Java Agent:注入 JVM,采集方法耗时、GC、线程等;
  • eBPF / 系统探针:在内核层采集网络、系统调用等;
  • Sidecar:在 Pod 旁挂一个容器做流量劫持与可观测性采集。
    常与 APM、监控、链路追踪(如 SkyWalking、Pinpoint)一起使用。

TSDB

TSDB(Time Series Database,时序数据库)是专门存储按时间排序的指标数据的数据库。数据点通常是「时间戳 + 指标名/标签 + 数值」。针对写多读少、按时间范围查询、降采样、保留策略等做了优化。Prometheus、InfluxDB、VictoriaMetrics、TimescaleDB 等都属于 TSDB,是监控、IoT 场景的常用存储。


k8s

k8sKubernetes 的缩写(K 和 s 之间有 8 个字母)。Kubernetes 是容器编排平台,用来部署、扩缩容、调度和管理容器化应用(如 Docker 容器),提供服务发现、负载均衡、自愈、配置与密钥管理、存储编排等。监控中常需要采集 k8s 的 Pod、Node、Service、Deployment 等资源指标和事件。


      ┌───────────────────────┐
      │   Alerting / 告警系统  │
      │  (Alertmanager /      │
      │   Grafana Alert 等)   │
      └─────────▲─▲─▲────────┘
                │ │ │
      ┌─────────┘ │ └─────────┐
      │           │           │
  Metrics       Logs       Traces
(Prometheus)   (Loki)     (Tempo)
      ▲           ▲           ▲
      │           │           │
  ┌───────────────────────────────┐
  │        你的 AI 系统服务        │
  └───────────────────────────────┘

Metrics(指标)指标:Prometheus、Mimir 等
Logs(日志)日志:Loki
Traces(链路追踪)追踪:Tempo

observability 可观测性

Metrics/Logs/Traces 是「可观测性(Observability)」的三大类数据

Metrics(指标监控):http_requests_total、http_request_duration_seconds 这种只是「入门级」的技术指标。实际项目里通常会拆成几类:
基础资源:CPU、内存、磁盘、带宽、连接数。
服务层:QPS、P95/P99 延迟、错误率、队列长度、线程池/协程池利用率。
依赖组件:数据库(QPS/慢查询/连接池)、缓存命中率、消息队列堆积、外部 API 调用耗时与错误。
业务指标(business metrics):下单数、支付成功率、TTS 调用次数、首包延迟、ASR 成功率等。

Tracing(分布式追踪):比如 OpenTelemetry + Tempo/Jaeger,主要是按「一次请求/一次会话」看整条调用链,包含 span、上下游服务、耗时拆分,并不只是一个“trace_count 指标”那么简单。

Alert(告警):本质是基于 metrics/logs/traces 写的规则,比如:
5 分钟内 P95 latency > 1s 持续 3 次;
5xx rate > 1% 持续 10 分钟;
success_rate(ASR) 低于某个阈值;
单个租户/门店的 QPS 异常暴涨等。


Metrics

第 1 步:丰富 Metrics(从“技术指标”扩展到“业务指标”)
目标:从「只有 HTTP QPS/延迟」升级到「能反映 AI 业务健康的指标」。
1.梳理你关心的业务指标(business metrics),例如:

  • 每分钟 TTS 调用次数、成功率、首包延迟 P95;
  • 每个会话的轮次、平均对话时长;
  • 识别成功率、NLP 调用错误率等。
    2.在关键代码路径里打点(仍然用 prometheus_client):
  • 业务计数用 Counter;
  • 业务耗时/延迟用 Histogram;
  • 少量状态值用 Gauge(例如当前活动会话数)。
    3.在 Grafana 新增 Panel:
  • 使用这些新 metrics 写简单 PromQL,比如 rate(ai_tts_requests_total[5m])、histogram_quantile(0.95, …)。

首先需要在原有的FastAPI中暴露Metrics
目标:让服务在“http://localhost:8000/metrics”暴露Prometheus文本格式指标(包括请求总数、请求耗时)
1.定义指标
- 我需要回答什么问题
- 当前接口的qps
- p95延迟
- 错误率?
- 每次对话的平均轮次
- 这个问题更像“次数/速率”还是“耗时/分布”
- 次数/速率 -> counter
- 耗时/延迟分布 -> histogram
- 当前值(队列长度、连接数)-> Gauge
- 我需要按哪些维度区分(labels)
- 比如:按method、path、status区分http请求
- 或按model_name、tenant、scene区分AI请求

2.路径归一化

  • 为什么要做路径归一化
    • 如果label里面使用原始路径(/user/1001、/user/1002 都会变成不同的 path 取值。)
    • 路径一多序列数爆炸(高基数),Prometheus存不下、查不动,而且你其实只想看【按接口/路由】的QPS、延迟、不需要按每个ID区分
  • 具体怎么做
    • 把路径里面的变量改成占位符
    • (这个还没有实际操作过)

3.然后就是定义metrics和prometheus中间件
-埋点:在 FastAPI 里加一个 HTTP 中间件,对每次请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# Prometheus 打点中间件(对 /metrics 请求跳过打点,避免抓取影响业务指标)
app.add_middleware(PrometheusMiddleware)
@app.get("/metrics")
async def metrics():
data = generate_latest(REGISTRY)
return Response(content=data,
media_type=CONTENT_TYPE_LATEST)

@app.post("/api/compare") # 定义 POST 路由,路径为 /api/compare
async def compare_endpoint(payload: CompareRequest):
"""
对多份文档的关键信息提取结果进行对比。
"""
start = time.perf_counter() # 记录函数开始时间,用于后续统计耗时
try:
# 在线程池中异步执行 compare_documents,比直接 await 阻塞少,适合包含多次 LLM 推理等耗时操作
result = await run_in_threadpool(compare_documents, payload.documents)
tender_compare_total.inc() # 请求计数器 +1,用于统计 compare 接口被调用的总次数
tender_compare_duration_seconds.observe(time.perf_counter() - start) # 记录本次 compare 请求的耗时(单位:秒)
return result # 返回比对结果给客户端
except Exception:
tender_compare_total.inc() # 出现异常时也要把 compare 的调用数 +1(可扩展为按 status label 区分)
tender_compare_duration_seconds.observe(time.perf_counter() - start) # 也记录异常请求的耗时
raise # 重新抛出异常,让上层框架处理错误响应
  • 暴露 /metrics:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    # -*- coding: utf-8 -*-
    """
    Prometheus 指标定义模块。

    Grafana 通过 Prometheus 数据源拉取 /metrics 接口时,会拿到这里定义的指标。
    - Counter:只增不减的计数,适合请求次数、错误次数等。
    - Histogram:可统计分布与分位数,适合延迟、大小等,会生成 _bucket、_sum、_count 等时序。
    """
    from prometheus_client import (
    CollectorRegistry, # 指标的「注册表」,用来集中管理要暴露的指标
    Counter, # 只增不减的计数器类型
    Gauge, # 可增可减,用于当前进行中请求数(并发)
    Histogram, # 直方图类型,可记录分布、算分位数
    generate_latest, # 把注册表里的指标序列化成 Prometheus 文本格式(供 /metrics 返回)
    CONTENT_TYPE_LATEST, # Prometheus 规定的响应头:text/plain; version=0.0.4; charset=utf-8
    )

    # ---------------------------------------------------------------------------
    # 自定义 Registry:只暴露本应用定义的指标,不包含 Python 进程自带的 process_* 等
    # ---------------------------------------------------------------------------
    REGISTRY = CollectorRegistry()
    # CollectorRegistry() 创建一个「空注册表」。后面所有指标都要 register 到这里,
    # /metrics 里用 generate_latest(REGISTRY) 时,只会导出这个表里的内容。

    # ---------------------------------------------------------------------------
    # HTTP 通用指标(由中间件在每个请求上打点)
    # ---------------------------------------------------------------------------

    # 请求总数:每来一个请求就 +1,按 方法、路径、状态码 区分,便于在 Grafana 里看 QPS、错误率
    http_requests_total = Counter(
    "http_requests_total", # 指标名,在 /metrics 输出和 Prometheus 查询里用这个名字
    "Total HTTP requests", # 指标的说明文字(help),在 Prometheus 里会显示
    ["method", "path", "status"], # 维度(label):同一指标下按 method/path/status 分多条时序
    )
    # 例如会生成:http_requests_total{method="GET",path="/api/health",status="200"} 5

    # 请求耗时(秒):每个请求记一次耗时,按 方法、路径 区分,buckets 用于算 P50/P95/P99
    http_request_duration_seconds = Histogram(
    "http_request_duration_seconds",
    "HTTP request latency in seconds",
    ["method", "path"],
    buckets = (
    0.1, 0.5, 1.0, 2.0, 5.0,
    10.0, 30.0, 60.0, 120.0, 300.0, 600.0
    )
    )
    # Histogram 会生成多条时序:_bucket(各区间计数)、_sum(总耗时)、_count(请求数)

    # 当前进行中的请求数(并发):请求进入时 +1,结束时 -1,按 method、path 区分
    http_requests_active = Gauge(
    "http_requests_active",
    "Number of HTTP requests in progress",
    ["method", "path"],
    )

    # ---------------------------------------------------------------------------
    # 业务指标(在具体接口里手动打点)
    # ---------------------------------------------------------------------------

    # 标书文件上传次数,按 成功/失败/错误 区分
    tender_uploads_total = Counter(
    "tender_uploads_total",
    "Total file uploads (success/failed/error)",
    ["status"], # 只有 status 一个维度,取值如 "success" / "failed" / "error"
    )

    # 标书解析耗时(秒),仅统计实际做了解析的请求(PDF 或 MD 处理时间)
    tender_parse_duration_seconds = Histogram(
    "tender_parse_duration_seconds",
    "PDF/MD parse duration in seconds",
    buckets = (
    0.1, 0.5, 1.0, 2.0, 5.0,
    10.0, 30.0, 60.0, 120.0, 300.0, 600.0
    )
    )

    # 文档对比次数与耗时
    tender_compare_total = Counter(
    "tender_compare_total",
    "Total document comparison calls",
    [], # 无 label,或加 ["status"] 按 success/error 分
    )

    tender_compare_duration_seconds = Histogram(
    "tender_compare_duration_seconds",
    "Document comparison duration in seconds",
    buckets=(0.1, 0.5, 1.0, 2.0, 5.0, 10.0, 30.0, 60.0, 120.0, 300.0, 600.0)
    )

    REGISTRY.register(tender_compare_total)
    REGISTRY.register(tender_compare_duration_seconds)
    # 把上面 4 个指标都注册到 REGISTRY,这样 generate_latest(REGISTRY) 时才会把它们导出
    REGISTRY.register(http_requests_total)
    REGISTRY.register(http_request_duration_seconds)
    REGISTRY.register(http_requests_active)
    REGISTRY.register(tender_uploads_total)
    REGISTRY.register(tender_parse_duration_seconds)


    # -*- coding: utf-8 -*-
    """
    Prometheus 打点中间件。

    对每个 HTTP 请求自动记录:
    - http_requests_total:按 method、path、status 计数
    - http_request_duration_seconds:请求耗时
    - http_requests_active:请求进入时 +1、结束时 -1(并发数)

    为避免 Grafana/Prometheus 抓取 /metrics 时把这些抓取请求也算进业务 QPS,
    对 path == "/metrics" 的请求不做打点,直接放行。
    """
    import time
    from starlette.middleware.base import BaseHTTPMiddleware
    # BaseHTTPMiddleware:Starlette 提供的基类,实现 dispatch 即可在「请求前/后」插入逻辑

    from app.core.metrics import (
    http_requests_total, # 请求总数 Counter,用来 .inc()
    http_request_duration_seconds, # 请求耗时 Histogram,用来 .observe(秒数)
    http_requests_active, # 进行中请求数 Gauge,请求开始 .inc()、结束 .dec()
    )


    class PrometheusMiddleware(BaseHTTPMiddleware):
    """在请求前后记录 Prometheus 指标。"""

    async def dispatch(self, request, call_next):
    # request:当前请求对象(含 method、url 等)
    # call_next:调用下一个中间件或路由,返回 response;必须 await

    path = request.url.path
    # 请求路径,如 "/api/upload"、"/metrics"(不含查询串)

    method = request.method
    # 请求方法,如 "GET"、"POST"

    # 跳过 /metrics,避免监控抓取本身被计入请求数与延迟
    if path == "/metrics":
    return await call_next(request)
    # 直接交给下游处理并返回,下面不再打点

    # 默认状态码;若请求过程中抛异常,在 except 里保持 500
    status = 500

    start = time.perf_counter()
    # 记录开始时间(高精度),用于后面算耗时

    # 并发数:请求进入时 +1
    http_requests_active.labels(method=method, path=path).inc()

    try:
    response = await call_next(request)
    # 把请求交给后面的中间件/路由处理,并等待返回 response

    status = response.status_code
    # 从响应里取出状态码,如 200、404、500

    return response
    # 把响应返回给客户端(finally 会在 return 前执行)

    except Exception:
    status = 500
    # 任何未捕获异常都视为 500
    raise
    # 重新抛出,让上层处理;finally 仍会执行

    finally:
    # 无论 try 里是 return 还是 raise,finally 都会执行,保证「每个请求都打点一次」

    # 并发数:请求结束时 -1(与上面的 .inc() 成对)
    http_requests_active.labels(method=method, path=path).dec()

    duration = time.perf_counter() - start
    # 耗时(秒),浮点数

    http_requests_total.labels(
    method=method,
    path=path,
    status=status,
    ).inc()
    # .labels(...) 选中「这一组维度」的时序,.inc() 对该计数器 +1

    http_request_duration_seconds.labels(
    method=method,
    path=path,
    ).observe(duration)
    # .observe(duration) 把这次请求的耗时记进直方图,会更新 _bucket、_sum、_count

Logs

第 2 步:规范 Logs,并接入 Loki(日志)
目标:从「FastAPI 在控制台随便打日志」升级到「结构化日志 + 集中检索」。
1.应用内统一日志格式(FastAPI / Python logging):

  • 设置统一 logger,带上 level、timestamp、request_id/session_id、path 等;
  • 优先使用 JSON 格式,方便 Loki/ELK 解析。
    2.选择日志管道到 Loki:
  • 最常见:用 Promtail 作为 agent,去读容器 stdout 或日志文件,然后推送到 Loki。
  • 配置 Promtail:指定 scrape_configs(采集路径、标签,如 job, service)。
    3.Grafana 里配置 Loki 数据源:
  • 在 Grafana Data source 里新增 Loki;
  • 用 Explore 做 logQL 查询(比如 {app=”kiosk-backend”} |= “ERROR”)。

Traces

第 3 步:接入 Traces(分布式追踪 / OpenTelemetry + Tempo)
目标:从「只能看单点日志」升级到「一条请求在各个组件之间的完整链路」。
1.选一个追踪后端:

  • 在 Grafana 生态里,通常选 Tempo(Tracing),也可以先用 Jaeger 简单上手。
    2.在 FastAPI 里接入 OpenTelemetry SDK:
  • 安装 opentelemetry-sdk、opentelemetry-instrumentation-fastapi、opentelemetry-exporter-otlp 等;
  • 在应用启动时初始化 TracerProvider,配置 OTLP 导出到 Tempo/Jaeger;
  • 可选:给关键函数/远程调用(比如调大模型、调 TTS)手动加 span。
    3.在 Grafana 中添加 Tempo 数据源:
  • 然后可以在 Explore 的 Traces 视图中按 service / span name / duration 搜索;
  • 也可以在面板中做「metrics drill-down 到具体 trace」的联动。

Alert

第 4 步:基于 Metrics/Logs/Traces 建 Alert(告警)
目标:从「人肉看大盘」升级到「出了问题自动通知」。
1.基于 Metrics 的告警(最重要):

  • 在 Prometheus 中写告警规则(alerting rules),例如:
  • P95 latency > 1s 持续 5 分钟;
  • 5xx rate > 1% 持续 10 分钟;
  • ai_tts_success_rate < 0.9。
  • 把这些规则推给 Alertmanager,再由 Alertmanager 发到飞书/钉钉/Slack/邮箱。
  • 或者在 Grafana Alerting 中直接基于 PromQL 创建告警。
    2.基于 Logs 的告警(补充):
  • 在 Loki 上做查询(例如 count 5 分钟内 ERROR),用 Grafana Alert 触发;
  • 适合 catch 特殊错误文案、异常堆栈。
    3.基于 Traces 的告警(更进阶):
  • 一般较少直接对 traces 告警,而是对传统 metrics 告警后再用 trace 排查;
  • 但也可以在某些系统中对「长尾 span」做统计派生出 metrics,再告警。

压测工具:hey wrk
$$

Grafana生态实现不是一个单一工具,而是一整套围绕Grafana作为可观测性可视化与运维入口的技术栈

简单说:

你所有的metrics/logs/tracing/alert最终都在Grafana里统一查看和管理

Grafana是一个可视化与统一看板

  • 角色:可视化与仪表盘平台
  • 主要做:
    • 连接多种数据源(Prometheus、Loki、Tempo、MySQL、Elasticsearch等)
    • 用图标、表格、单值等组件做Dashboard;
    • 配置告警(如阈值、通知渠道等)
    • 变量、时间范围、链接等

Prometheus 指标存储与查询

  • 角色:时序数据库+抓取(scrape)引擎+查询引擎。
  • 主要做:
    • 按配置定位拉取各服务的/metrics(或exporter),把指标存成时序数据;
    • 用PromQL做聚合、算rate、分位数等;
    • 可配置告警规则,把告警发给Alertmanager等。
  • 特点:专注[采集 存储 查询]

一、整体架构

监控链路可以概括为三条腿:

1
2
3
4
┌─────────────────┐      scrape       ┌─────────────┐      query      ┌─────────────┐
│ Backend (FastAPI) │ ──────────────► │ Prometheus │ ◄──────────── │ Grafana │
│ GET /metrics │ 每 15s 拉取 │ :9090 │ 作图与告警 │ :3000 │
└─────────────────┘ └─────────────┘ └─────────────┘

实现原理是在具体的Http服务上注册一个GET /metrics路由,每次被访问时返回
单机部署架构
服务拆分
Docker设计
Metrics / Tracing / Alert
日志体系
运行拓扑
真实工程描述

metrics 指标 系统的体征监控

  • 当前gpu使用率 当前延迟 当前并发数
  • AI特有监控

推理时延
token/s
队列长度
会话并发数

tracing 链路追踪 一次请求的全过程录像
在你系统里的tracing
Audio Input

ASR

LLM

TTS

Websocket Streaming

Tracing 会记录:

ASR: 480ms
LLM: 3200ms
TTS: 900ms
Network: 120ms

alert 告警 系统出事自动通知

系统级

GPU > 95% 持续5分钟
websocket断开率 > 20%
CPU > 90%

AI

LLM延迟 > 5秒
token/s < 5
ASR失败率 > 10%
session异常率 > 3%

告警形式

飞书
企业微信
Slack
邮件