内存诊断
网关内存上涨时,需要先知道是进程的哪一部分占用了内存,才能决定是加内存、限制流量,还是上报缺陷。AISIX 为此提供三种来源:
- Prometheus 端点上的内存指标。它们展示分配器和进程持有多少内存、进程所受的内存上限,以及每个进程内存储的占用情况。
- 按需采集的堆 profile。独立的、默认只绑定回环地址的诊断监听器上的
GET /debug/pprof/heap返回当前堆的 profile,可用go tool pprof读取。 - 自动写出的堆 profile。常驻内存接近内存上限时,网关会自动写出 profile,使证据在随后的内存溢出(OOM)终止后仍然保留。
三者默认全部开启,无需在控制面配置。它们在网关自身的启动配置中设置,开源网关和连接到 AISIX Cloud 的网关用法相同。
读取内存指标
这些指标与其他指标一起在指标监听器上提供。它们在 Prometheus 抓取端点时读取,从不在请求路径上读取,每个标签的 取值都是固定集合。所有序列请参阅指标参考。
curl -sS "http://127.0.0.1:9090/metrics" \
| grep -E '^(aisix_allocator_bytes|aisix_memory_limit_bytes|process_resident_memory_bytes|aisix_component_|aisix_runtime_)'
这些指标分为以下几组:
| 指标 | 含义 |
|---|---|
aisix_allocator_bytes{stat} | 内存分配器统计的字节数:allocated(存活对象)、active、resident、mapped、retained 和 metadata。allocated 持续上升说明网关持有的对象变多;resident 远高于 allocated 说明存在碎片,或已释放的页尚未归还操作系统。 |
process_resident_memory_bytes 及其他 process_* 序列 | 内核视角下的进程,使用 Prometheus 标准进程采集器的名称,因此现有进程看板和告警无需修改即可使用。 |
aisix_memory_limit_bytes | 容器的内存上限,读取自 cgroup v2 的 memory.max 或 cgroup v1 的 memory.limit_in_bytes。进程没有上限时不存在该序列。 |
aisix_component_entries{component} 和 aisix_component_bytes{component} | 每个进程内存储持有的条目数;存储能精确统计字节时,还给出字节数。 |
aisix_runtime_alive_tasks{runtime} 和 aisix_runtime_global_queue_depth{runtime} | 每个异步运行时上存活的任务数,以及排队但尚未被取走的任务数。runtime 为 control,并为每个每核一线程 Worker 增加 tpc-0 到 tpc-N。 |
分配器和堆 profile 相关序列仅由官方 AISIX 镜像中的 Linux 构建发出。process_* 序列在 Linux 上发出。
应当告警的是常驻内存占内存上限的比例:
process_resident_memory_bytes / aisix_memory_limit_bytes
找出占用内存的部分
先看组件指标,再与流量对照。每个存储指向不同的原因:
| 信号 | 可能原因 | 处理方式 |
|---|---|---|
aisix_component_bytes{component="in_flight_request_bodies"} 与 aisix_proxy_in_flight_requests 一同上升 | 大量大请求(例如内联图片或文档的请求)在等待慢上游时被保留在内存中。 | 按这类请求规划内存并限制其并发。参见为大请求体规划内存。 |
aisix_component_bytes{component="guardrail_holdback"} 偏高 | 为输出安全护栏而保留的流式响应。 | 检查安全护栏的流式窗口。参见流式输出。 |
aisix_component_entries{component="metric_series"} 持续增长 | 标签基数:大量不同的标签值(例如 API Key、用户或模型)各自产生新的指标序列。 | 减少发布的标签。参见聚合与时间序列数量。 |
aisix_component_entries{component="exporter_queue"} 或 usage_event_queue 持续偏高 | 接收端缓慢或不可用:记录在内存中排队的速度超过投递速度。exporter 标签给出导出器名称。 | 检查目标端健康状况,以及导出器的丢弃和失败计数。 |
aisix_component_entries{component="log_queue"} 持续偏高 | 收集 网关标准错误流的组件跟不上。 | 检查日志收集组件以及 aisix_log_lines_dropped_total。 |
aisix_component_entries{component="snapshot_pending_reclaim"} 持续大于 0 | 长时间运行的请求仍持有已退役的配置快照。在这些请求结束之前,每个快照都占用一整份配置的内存。 | 配置变更后短暂出现属于正常。若持续存在,请排查超长的流或卡住的请求。 |
aisix_runtime_alive_tasks 增长而流量不变 | 任务泄漏。 | 采集一份堆 profile,并连同指标历史一起上报。 |
其余组件统计的存储都受配置约束:response_cache 和 semantic_cache(本进程内持有的条目,不含 Redis 中的条目)、budget_cache、route_embedding_cache、ratelimit_local_keys(内存限流后端的键,使用 Redis 时为 0),以及 upstream_clients(为带有独立连接设置的服务提供方密钥构建的 HTTP 客户端)。
exporter 只在 component="exporter_queue" 上设置,其他组件为空。导出器删除后,其序列降为 0。
如果没有哪个存储能解释增长,或者 aisix_allocator_bytes{stat="allocated"} 上升而所有组件保持平稳,请采集堆 profile。
采集堆 profile
网关从启动起就对堆分配进行采样,因此可以直接从已经出现异常的网关上采集 profile,无需重启。平均每分配 2 MiB 记录一次分配及其调用栈。实测开销在网关基准测试的噪声范围之内。