☰
工业网关 Metrics 暴露实战:Prometheus 指标设计与查询
2026/10/15 16:16:04 网站建设 项目流程

工业网关承担设备接入、协议转换、数据处理、北向上报和远程运维等多类任务。一旦网关变成黑盒,现场只能回答“好像有问题”,却无法回答“问题在哪个环节、影响多少设备、持续多久、是否恢复”。

Metrics 的价值是把系统状态量化:请求量、错误率、延迟分布、连接数、队列长度、设备在线率、CPU 和内存压力都可以被持续采集、告警和趋势分析。本文从工程实战角度梳理工业网关的 Prometheus 指标暴露方案,覆盖指标类型、代码实现、端点配置、标签设计、PromQL 查询、RED / USE 模型、边缘约束和上线验收。

一、指标先回答哪些问题

在设计指标前,先明确工业网关最需要回答的问题:

视角关键问题典型指标
服务质量请求是否变慢、是否失败请求速率、错误率、延迟分布
设备接入设备是否在线、采集是否正常在线设备数、采集成功率、采集耗时
资源压力网关是否快被压垮CPU、内存、磁盘、网络、句柄
异步任务任务是否积压、重试是否失控队列长度、执行时长、重试次数
北向链路上报是否可用、断网是否恢复上报速率、失败数、发送耗时、缓冲量
版本演进新版本是否更差按版本标签对比关键指标

不要为了“看起来可观测”而添加大量无人消费的指标。每个指标都应有明确的使用场景:进仪表盘、进告警规则,或用于故障定位。

二、四种基础指标类型

Prometheus 客户端库中常见四种类型,选择错误会让后续查询和聚合变得困难。

类型语义适用场景查询要点
Counter从进程启动开始单调递增的累计值请求数、错误数、任务完成数、发送字节数用rate()或increase()计算速率
Gauge可上升也可下降的瞬时值在线设备数、活跃连接数、队列长度、内存占用直接取值,或用avg_over_time()、max_over_time()
Histogram将观测值放入累计桶,同时暴露_sum和_count请求耗时、采集耗时、报文大小用histogram_quantile()在服务端计算分位数
Summary客户端预计算分位数单进程内的耗时分布分位数不能跨实例正确聚合,分布式场景慎用

一个容易混淆的点是 Histogram 与 Summary:

  • Histogram 的桶在暴露时已经确定,Prometheus 可以先按实例聚合_bucket,再用histogram_quantile()计算 P95、P99;
  • Summary 的分位数在客户端计算完成,Prometheus 无法把多个实例的 P99 再平均成全局 P99;
  • 因此网关多实例、多站点场景优先选择 Histogram。

三、指标命名与标签设计

1. 命名规范

建议统一使用小写和下划线,并带基础单位:

类型命名示例说明
Counterhttp_requests_totalCounter 以_total结尾,便于识别
Histogramdevice_collect_duration_seconds自动生成_bucket、_sum、_count
Gaugedevice_online_count明确这是设备数量
字节数northbound_sent_bytes_total使用 bytes,不用 KB / MB
温度cpu_temperature_celsius带物理单位

同一含义不要在不同模块中混用latency、duration、cost。团队可以先约定:耗时统一用duration_seconds,速率统一交给 PromQL 计算,指标名本身不带rate。

2. 标签只放低基数字段

标签决定时间线数量。假设 500 个网关、2000 个设备、10000 个请求 ID:

字段是否适合作标签原因
method适合GET / POST 等有限取值
route适合使用路由模板,如/devices/{id}
status_class适合2xx、4xx、5xx等有限分类
protocol适合Modbus、OPC UA、MQTT 等有限枚举
result适合success、timeout、protocol_error 等受控枚举
site_id视规模适合站点数量受控时可用
device_id通常不适合设备多、扩容后时间线爆炸
request_id不适合几乎每个请求都不同
user_id不适合高基数且涉及隐私
原始 URL / path不适合路径中可能带 ID,取值不可控

正确做法是:

  • Metrics 保留聚合维度,例如method、route、result;
  • 具体的request_id、device_id、错误详情写入日志或 Trace;
  • 通过 Exemplar、Trace ID 或统一事件 ID 在异常时下钻。

如果确实需要按设备看状态,可以只对关键状态建聚合指标,例如device_online_count{site_id, protocol};不要为每个设备的高频指标都创建完整时间线。

四、Python 网关中的基础实现

下面以 FastAPI +prometheus-client为例。代码重点是:使用路由模板作为route标签,不把原始 URL 放入标签。

importtimefromcollections.abcimportAwaitable,CallablefromfastapiimportFastAPI,Request,Responsefromprometheus_clientimportCounter,Gauge,Histogram,make_asgi_app app=FastAPI()http_requests_total=Counter("http_requests_total","Total HTTP requests",["method","route","status_class"],)http_request_duration_seconds=Histogram("http_request_duration_seconds","HTTP request duration in seconds",["method","route"],buckets=[0.005,0.01,0.025,0.05,0.1,0.25,0.5,1,2.5,5,10],)device_collect_duration_seconds=Histogram("device_collect_duration_seconds","Device data collection duration in seconds",["protocol","result"],buckets=[0.005,0.01,0.025,0.05,0.1,0.25,0.5,1,2.5,5,10],)device_online_count=Gauge("device_online_count","Number of online devices",["site_id","protocol"],)defstatus_class(status_code:int)->str:ifstatus_code<200orstatus_code>=600:return"unknown"returnf"{status_code//100}xx"@app.middleware("http")asyncdefinstrument_http_requests(request:Request,call_next:Callable[[Request],Awaitable[Response]],)->Response:started_at=time.perf_counter()response=awaitcall_next(request)route=request.scope.get("route")route_name=getattr(route,"path","unmatched")http_requests_total.labels(method=request.method,route=route_name,status_class=status_class(response.status_code),).inc()http_request_duration_seconds.labels(method=request.method,route=route_name,).observe(time.perf_counter()-started_at)returnresponse metrics_app=make_asgi_app()app.mount("/prometheus",metrics_app)

这里刻意将挂载点设为/prometheus。make_asgi_app()内部会在/metrics路径输出指标,因此最终抓取地址是:

http://gateway:8080/prometheus/metrics

如果写成app.mount("/metrics", make_asgi_app()),实际抓取路径通常会变成/metrics/metrics,这是现场最常见的配置错误之一。

Gauge 示例:在线设备数

Gauge 适合表达“当前状态”,例如:

device_online_count.labels(site_id="site-01",protocol="modbus_tcp",).inc()device_online_count.labels(site_id="site-01",protocol="modbus_tcp",).dec()

如果设备数量会周期性重算,也可以不使用inc()/dec(),而是在每轮发现后直接set():

device_online_count.labels(site_id="site-01",protocol="modbus_tcp",).set(len(online_devices))

Histogram 示例:设备采集耗时

asyncdefcollect_device(protocol:str,device_id:str)->None:started_at=time.perf_counter()result="success"try:awaitread_device(device_id)exceptTimeoutError:result="timeout"raiseexceptException:result="error"raisefinally:device_collect_duration_seconds.labels(protocol=protocol,result=result,).observe(time.perf_counter()-started_at)

device_id不进入指标标签,但应写入日志和 Trace,便于异常下钻。

五、端点暴露与 Prometheus 抓取配置

假设网关服务监听8080,指标端点为/prometheus/metrics,Prometheus 配置如下:

global:scrape_interval:15sevaluation_interval:15sscrape_configs:-job_name:edge-gatewaymetrics_path:/prometheus/metricsstatic_configs:-targets:-gateway-1:8080-gateway-2:8080-gateway-3:8080labels:env:prodregion:cn-east

如果网关在内网,Prometheus 无法直接访问,可以考虑:

方案适用情况注意点
本地 Prometheus 抓取边缘节点资源充足注意磁盘保留周期和告警转发
Prometheus Agent 模式 + Remote Write多站点集中观测需要断网缓冲、重试和上限控制
OTel Collector / Alloy 中转Logs、Traces、Metrics 统一采集统一处理重试、限速和身份标签
Pushgateway短生命周期批任务不适合长期高频状态指标

工业现场网络中断不可避免。指标链路要明确:

  1. 本地最多缓存多久;
  2. 远端恢复后如何补传;
  3. 缓冲写满时先丢弃什么;
  4. 断网期间告警如何降级;
  5. 恢复后如何标记数据缺口。

六、默认进程与运行时指标

Pythonprometheus-client会自动暴露一批进程和运行时指标,常见如下:

指标类型用途
process_cpu_seconds_totalCounter进程 CPU 累计耗时
process_resident_memory_bytesGauge常驻内存
process_virtual_memory_bytesGauge虚拟内存
process_open_fdsGauge打开的文件描述符
process_start_time_secondsGauge进程启动时间
python_gc_objects_collected_totalCounterGC 回收对象数

这些指标可以补足进程视角,但还不能替代系统视角。建议同时采集:

  • CPU 使用率与负载;
  • 内存可用量;
  • 磁盘容量、读写耗时;
  • 网络收发量和错误包;
  • TCP 连接状态;
  • 进程句柄和线程数;
  • 容器或 systemd 服务状态。

七、常用 PromQL 查询

1. 请求速率

rate()只适用于 Counter:

sum(rate(http_requests_total[5m]))

按路由查看吞吐量:

sum by (route) ( rate(http_requests_total[5m]) )

2. HTTP 错误率

示例使用status_class="5xx":

sum by (route) ( rate(http_requests_total{status_class="5xx"}[5m]) ) / sum by (route) ( rate(http_requests_total[5m]) )

如果希望输出百分比,再乘以100。分母可能为 0 时,告警规则中应显式处理,避免无流量接口产生误判。

3. P99 延迟

Histogram 必须先聚合_bucket,再计算分位数,并保留le标签:

histogram_quantile( 0.99, sum by (le, route) ( rate(http_request_duration_seconds_bucket[5m]) ) )

不要把各实例的 P99 直接平均。那不是全局 P99,而是“分位数的平均值”。

4. Top N 路由

先聚合,再取 Top N:

topk( 10, sum by (route) ( rate(http_request_duration_seconds_count[5m]) ) )

如果先topk()再聚合,可能把本应进入全局 Top N 的时间线提前过滤掉。

5. 设备在线率

假设同时暴露device_online_count和device_registered_count:

sum by (site_id, protocol) (device_online_count) / sum by (site_id, protocol) (device_registered_count)

6. 采集成功率

示例中device_collect_duration_seconds_count按result分桶:

sum by (protocol) ( rate(device_collect_duration_seconds_count{result="success"}[5m]) ) / sum by (protocol) ( rate(device_collect_duration_seconds_count[5m]) )

八、RED 与 USE:建立覆盖面

RED 适合服务接口和北向链路,USE 适合资源对象。两者结合可以避免只监控接口而忽略网关自身压力。

1. RED

元素含义示例
Rate吞吐速率HTTP QPS、设备采集次数 / 秒、北向上报次数 / 秒
Errors错误5xx、协议超时、校验失败、上报失败
Duration耗时分布HTTP 耗时、采集耗时、规则执行耗时、上报耗时

2. USE

元素含义示例
Utilization使用率CPU、内存、磁盘空间、带宽
Saturation饱和度队列长度、等待任务数、连接池等待、磁盘 IO 等待
Errors错误网络错误、读写失败、连接失败、句柄耗尽

一个工业网关的最小指标集合可以是:

类别指标
HTTPhttp_requests_total、http_request_duration_seconds
设备采集device_collect_duration_seconds、device_protocol_errors_total
设备状态device_online_count、device_registered_count
任务队列task_queue_length、task_duration_seconds、task_failures_total
北向上报northbound_messages_total、northbound_duration_seconds、northbound_buffer_size
资源CPU、内存、磁盘、网络、FD、进程重启次数

九、告警不是阈值堆叠

指标暴露后,常见做法是把所有阈值都配一遍,结果告警过多,现场逐渐忽略。更好的方式是先定义症状,再定位原因。

告警建议规则方向说明
网关不可达up == 0持续 2 分钟区分单实例和网络分区
HTTP 错误率高5xx 占比持续超过阈值结合流量下限
延迟劣化P95 或 P99 持续升高关注趋势,不只看瞬时值
设备采集失败采集失败率或超时率突增按协议和站点分组
队列积压队列长度持续增长配合消费速率下降判断
北向断链上报成功率下降或缓冲增长区分本地故障与远端不可用
资源压力内存、磁盘、FD 接近上限磁盘满会影响业务和日志
进程重启启动时间变化频繁需要结合崩溃日志

告警内容应带上网关、站点、版本和协议等身份信息,并链接到对应仪表盘或排障手册。

十、部署与验收清单

1. 应用侧

  • 指标端点可访问,路径与 Prometheus 配置一致;
  • 输出包含HELP和TYPE;
  • Counter 使用_total命名;
  • 耗时使用秒,字节使用 bytes;
  • Histogram 桶覆盖正常值、慢请求和超时场景;
  • HTTP 标签使用路由模板,不使用原始 URL;
  • device_id、request_id等高基数信息不进标签;
  • 指标注册只执行一次,进程重启后历史值允许归零;
  • 指标采集失败不影响业务主流程;
  • 关键业务路径均有 Rate、Errors、Duration 指标。

2. 采集侧

  • up == 1,抓取时长在合理范围;
  • 网关、站点、环境、版本身份完整;
  • 时间线数量有预算和告警;
  • 断网时本地缓存策略明确;
  • 恢复后数据能按时间对齐;
  • Remote Write 有重试、限速和上限;
  • Prometheus 磁盘保留周期经过容量测算。

3. 查询与运维侧

  • HTTP QPS、错误率、P95 / P99 有仪表盘;
  • 设备采集成功率和耗时可按站点、协议过滤;
  • 队列积压和北向缓冲可观测;
  • 常见告警有处置手册;
  • 指标异常可以跳转日志或 Trace;
  • 升级前后可按版本对比;
  • 定期清理不再使用的指标和仪表盘。

十一、常见坑与应对

坑现场表现应对
抓取路径写错Prometheusup == 0,浏览器访问 404明确挂载点与内部/metrics的组合关系
原始 path 作标签时间线持续膨胀,内存和存储升高使用路由模板,未匹配请求归入unmatched
用户 / 请求 / 设备 ID 作标签Prometheus 指标爆炸高基数信息放日志和 Trace
Counter 用 avg 查询数值没有业务含义使用rate()或increase()
Summary 跨实例聚合全局分位数不准确多实例优先使用 Histogram
Histogram 先算分位数再聚合聚合维度丢失或计算错误先sum by (le, ...),再histogram_quantile()
先 topk 再聚合Top N 结果不准确先聚合,再topk()
桶设置不贴近业务全落在一两个桶,分位数无意义用真实耗时分布校准桶
无本地缓冲断网后数据缺口不可恢复Agent 模式落盘、限速、重试
指标无身份标签无法定位站点、实例和版本统一 job、instance、site、version 模型

TL;DR

工业网关的 Metrics 暴露先从语义正确的四类指标开始:Counter 看累计事件,Gauge 看当前状态,Histogram 看可聚合分布,Summary 慎用于多实例分位数。标签必须低基数,HTTP 使用路由模板,设备、请求和用户 ID 留在日志与 Trace 中。端点配置要确认真实抓取路径;PromQL 中 Counter 用rate(),Histogram 先聚合_bucket再计算分位数,Top N 先聚合再筛选。工程落地上用 RED 覆盖服务链路,用 USE 覆盖资源,用设备采集、任务队列、北向上报和资源指标补齐工业场景,并配套断网缓冲、容量规划、身份标签、仪表盘、告警和验收清单。

如果在工业边缘协议运行时中评估可观测能力,可以参考 Zenova EdgeOS 的 Prometheus 指标暴露、远程诊断和运行监测实践。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询