Python应用重启时如何保留Prometheus Gauge与Counter指标值?
解决Prometheus指标重启丢失的问题
默认情况下,Prometheus Python客户端的指标是内存存储的,应用重启后会重置为初始值。要维持指标数值,核心思路是把指标值持久化到外部存储,重启时从存储读取恢复。以下是几种可行方案:
方案1:持久化到JSON文件
这是最简单的方式,适合标签维度不多的场景。
实现步骤:
- 应用启动时,读取JSON文件中的历史指标值,恢复到Gauge中
- 定时(比如每30秒)将当前指标值写入JSON文件,或者在指标更新时触发保存
代码示例:
from prometheus_client import Gauge, start_http_server import json import time from threading import Timer # 定义持久化文件路径 PERSIST_FILE = "prom_metrics.json" # 初始化Gauge指标 visits = Gauge('visit_count', 'No. of connected customers', ['mobile']) def save_metrics(): """保存当前指标值到JSON文件""" metrics_data = {} # 遍历所有mobile标签的指标值 for label_values in visits._metrics: mobile = label_values[0] metrics_data[mobile] = visits.labels(mobile)._value.get() with open(PERSIST_FILE, 'w') as f: json.dump(metrics_data, f) # 定时30秒后再次保存 Timer(30.0, save_metrics).start() def load_metrics(): """从JSON文件加载历史指标值""" try: with open(PERSIST_FILE, 'r') as f: metrics_data = json.load(f) for mobile, value in metrics_data.items(): visits.labels(mobile).set(value) except FileNotFoundError: # 文件不存在时跳过,指标保持初始值0 pass if __name__ == "__main__": # 启动时加载历史数据 load_metrics() # 启动Prometheus HTTP服务 start_http_server(addr='X.X.X.X', port=8001) # 启动定时保存任务 save_metrics() # 模拟业务逻辑更新指标 while True: visits.labels("ios").inc() visits.labels("android").inc() time.sleep(1)
方案2:使用SQLite数据库存储
如果你的指标标签维度复杂,或者需要更可靠的存储,SQLite是轻量级的选择。
实现步骤:
- 创建SQLite表,存储指标名、标签键值、指标数值
- 指标更新时同步写入数据库
- 应用启动时查询数据库,恢复所有指标值
代码示例:
from prometheus_client import Gauge, start_http_server import sqlite3 import time from threading import Timer DB_NAME = "prom_metrics.db" visits = Gauge('visit_count', 'No. of connected customers', ['mobile']) def init_db(): """初始化SQLite数据库表""" conn = sqlite3.connect(DB_NAME) cursor = conn.cursor() cursor.execute(''' CREATE TABLE IF NOT EXISTS metrics ( metric_name TEXT, label_key TEXT, label_value TEXT, value REAL, PRIMARY KEY (metric_name, label_key, label_value) ) ''') conn.commit() conn.close() def save_metrics(): """保存当前指标到数据库""" conn = sqlite3.connect(DB_NAME) cursor = conn.cursor() # 先删除旧数据,再插入新数据 cursor.execute("DELETE FROM metrics WHERE metric_name = 'visit_count'") for label_values in visits._metrics: mobile = label_values[0] value = visits.labels(mobile)._value.get() cursor.execute(''' INSERT INTO metrics (metric_name, label_key, label_value, value) VALUES (?, ?, ?, ?) ''', ('visit_count', 'mobile', mobile, value)) conn.commit() conn.close() Timer(30.0, save_metrics).start() def load_metrics(): """从数据库加载指标""" conn = sqlite3.connect(DB_NAME) cursor = conn.cursor() cursor.execute("SELECT label_value, value FROM metrics WHERE metric_name = 'visit_count'") for mobile, value in cursor.fetchall(): visits.labels(mobile).set(value) conn.close() if __name__ == "__main__": init_db() load_metrics() start_http_server(addr='X.X.X.X', port=8001) save_metrics() # 模拟业务逻辑 while True: visits.labels("ios").inc() visits.labels("android").inc() time.sleep(1)
注意事项
- 持久化频率:不要过于频繁写入,避免IO性能问题,根据业务需求调整(比如30秒到5分钟一次)
- 并发安全:如果多个线程更新指标,需要注意文件/数据库的并发访问,可加锁处理
- 数据一致性:如果应用异常退出,最后一次的指标值可能没保存,可结合事务(数据库方案)或原子写入(文件方案,比如先写临时文件再替换)
内容的提问来源于stack exchange,提问作者Ankit PrabhatKumar
相关产品推荐
相关产品推荐

