工业网关承担设备接入、协议转换、数据处理、北向上报和远程运维等多类任务。一旦网关变成黑盒现场只能回答“好像有问题”却无法回答“问题在哪个环节、影响多少设备、持续多久、是否恢复”。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 与 SummaryHistogram 的桶在暴露时已经确定Prometheus 可以先按实例聚合_bucket再用histogram_quantile()计算 P95、P99Summary 的分位数在客户端计算完成Prometheus 无法把多个实例的 P99 再平均成全局 P99因此网关多实例、多站点场景优先选择 Histogram。三、指标命名与标签设计1. 命名规范建议统一使用小写和下划线并带基础单位类型命名示例说明Counterhttp_requests_totalCounter 以_total结尾便于识别Histogramdevice_collect_duration_seconds自动生成_bucket、_sum、_countGaugedevice_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 appFastAPI()http_requests_totalCounter(http_requests_total,Total HTTP requests,[method,route,status_class],)http_request_duration_secondsHistogram(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_secondsHistogram(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_countGauge(device_online_count,Number of online devices,[site_id,protocol],)defstatus_class(status_code:int)-str:ifstatus_code200orstatus_code600:returnunknownreturnf{status_code//100}xxapp.middleware(http)asyncdefinstrument_http_requests(request:Request,call_next:Callable[[Request],Awaitable[Response]],)-Response:started_attime.perf_counter()responseawaitcall_next(request)routerequest.scope.get(route)route_namegetattr(route,path,unmatched)http_requests_total.labels(methodrequest.method,routeroute_name,status_classstatus_class(response.status_code),).inc()http_request_duration_seconds.labels(methodrequest.method,routeroute_name,).observe(time.perf_counter()-started_at)returnresponse metrics_appmake_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_idsite-01,protocolmodbus_tcp,).inc()device_online_count.labels(site_idsite-01,protocolmodbus_tcp,).dec()如果设备数量会周期性重算也可以不使用inc()/dec()而是在每轮发现后直接set()device_online_count.labels(site_idsite-01,protocolmodbus_tcp,).set(len(online_devices))Histogram 示例设备采集耗时asyncdefcollect_device(protocol:str,device_id:str)-None:started_attime.perf_counter()resultsuccesstry:awaitread_device(device_id)exceptTimeoutError:resulttimeoutraiseexceptException:resulterrorraisefinally:device_collect_duration_seconds.labels(protocolprotocol,resultresult,).observe(time.perf_counter()-started_at)device_id不进入指标标签但应写入日志和 Trace便于异常下钻。五、端点暴露与 Prometheus 抓取配置假设网关服务监听8080指标端点为/prometheus/metricsPrometheus 配置如下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短生命周期批任务不适合长期高频状态指标工业现场网络中断不可避免。指标链路要明确本地最多缓存多久远端恢复后如何补传缓冲写满时先丢弃什么断网期间告警如何降级恢复后如何标记数据缺口。六、默认进程与运行时指标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()只适用于 Countersum(rate(http_requests_total[5m]))按路由查看吞吐量sum by (route) ( rate(http_requests_total[5m]) )2. HTTP 错误率示例使用status_class5xxsum by (route) ( rate(http_requests_total{status_class5xx}[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 Ntopk( 10, sum by (route) ( rate(http_request_duration_seconds_count[5m]) ) )如果先topk()再聚合可能把本应进入全局 Top N 的时间线提前过滤掉。5. 设备在线率假设同时暴露device_online_count和device_registered_countsum 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{resultsuccess}[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和TYPECounter 使用_total命名耗时使用秒字节使用 bytesHistogram 桶覆盖正常值、慢请求和超时场景HTTP 标签使用路由模板不使用原始 URLdevice_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 指标爆炸高基数信息放日志和 TraceCounter 用 avg 查询数值没有业务含义使用rate()或increase()Summary 跨实例聚合全局分位数不准确多实例优先使用 HistogramHistogram 先算分位数再聚合聚合维度丢失或计算错误先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 指标暴露、远程诊断和运行监测实践。