本文目录导读:

要统计分布式同步的整体指标,需要结合分布式系统的特点进行设计,以下是几种常见的方法和实现思路:
核心指标定义
from dataclasses import dataclass
from typing import Dict, List
import time
@dataclass
class SyncMetrics:
total_nodes: int
active_nodes: int
sync_completed: int
sync_failed: int
avg_sync_time: float
max_sync_time: float
min_sync_time: float
data_volume_mb: float
sync_rate: float # 成功/总请求
latency_p99: float
latency_p95: float
分布式指标采集方案
中心化采集器
import asyncio
import aiohttp
from collections import defaultdict
class CentralizedMetricCollector:
def __init__(self, config_path='nodes_config.yaml'):
self.nodes = self.load_nodes(config_path)
self.metrics_buffer = defaultdict(list)
async def collect_from_node(self, node_url):
"""从单个节点采集指标"""
try:
async with aiohttp.ClientSession() as session:
async with session.get(
f"{node_url}/metrics",
timeout=aiohttp.ClientTimeout(total=5)
) as response:
metrics = await response.json()
return metrics
except Exception as e:
print(f"Failed to collect from {node_url}: {e}")
return None
async def collect_all_metrics(self):
"""并行采集所有节点指标"""
tasks = [self.collect_from_node(node['url'])
for node in self.nodes]
results = await asyncio.gather(*tasks, return_exceptions=True)
return [r for r in results if r is not None]
def aggregate_metrics(self, all_metrics: List[Dict]):
"""聚合所有节点指标"""
aggregated = {
'total_nodes': len(all_metrics),
'total_sync_count': sum(m.get('sync_count', 0) for m in all_metrics),
'total_sync_errors': sum(m.get('error_count', 0) for m in all_metrics),
'avg_response_time': sum(m.get('avg_time', 0) for m in all_metrics) / len(all_metrics),
'total_data_volume': sum(m.get('data_volume', 0) for m in all_metrics)
}
# 计算百分位延迟
all_latencies = []
for m in all_metrics:
all_latencies.extend(m.get('latency_samples', []))
if all_latencies:
all_latencies.sort()
n = len(all_latencies)
aggregated['p95_latency'] = all_latencies[int(n * 0.95)]
aggregated['p99_latency'] = all_latencies[int(n * 0.99)]
# 计算同步成功率
success = aggregated['total_sync_count'] - aggregated['total_sync_errors']
aggregated['sync_success_rate'] = success / max(aggregated['total_sync_count'], 1) * 100
return aggregated
def load_nodes(self, config_path):
import yaml
with open(config_path, 'r') as f:
config = yaml.safe_load(f)
return config['nodes']
使用消息队列(推荐)
import json
import redis
from typing import Dict, Any
class MessageQueueCollector:
def __init__(self, redis_host='localhost', redis_port=6379):
self.redis_client = redis.Redis(
host=redis_host,
port=redis_port,
decode_responses=True
)
self.metrics_key = 'distributed:sync:metrics'
def push_metrics(self, node_id: str, metrics: Dict[str, Any]):
"""节点推送指标到消息队列"""
message = {
'node_id': node_id,
'timestamp': time.time(),
'metrics': metrics
}
self.redis_client.lpush(self.metrics_key, json.dumps(message))
def pull_metrics(self, batch_size=100) -> List[Dict]:
"""从消息队列拉取指标"""
messages = []
for _ in range(batch_size):
msg = self.redis_client.rpop(self.metrics_key)
if msg:
messages.append(json.loads(msg))
else:
break
return messages
def get_real_time_stats(self) -> Dict:
"""获取实时统计"""
recent_metrics = self.pull_metrics(batch_size=1000)
if not recent_metrics:
return {}
# 按节点分组
node_metrics = defaultdict(list)
for msg in recent_metrics:
node_metrics[msg['node_id']].append(msg['metrics'])
# 计算每个节点的统计
node_stats = {}
for node_id, metrics_list in node_metrics.items():
node_stats[node_id] = self.calculate_node_stats(metrics_list)
return {
'node_count': len(node_stats),
'nodes': node_stats,
'overall': self.calculate_overall_stats(node_stats)
}
def calculate_node_stats(self, metrics_list: List[Dict]) -> Dict:
"""计算单个节点统计"""
sync_times = [m.get('sync_time', 0) for m in metrics_list]
return {
'total_syncs': len(metrics_list),
'avg_sync_time': sum(sync_times) / len(sync_times) if sync_times else 0,
'max_sync_time': max(sync_times),
'min_sync_time': min(sync_times)
}
def calculate_overall_stats(self, node_stats: Dict[str, Dict]) -> Dict:
"""计算整体统计"""
total_syncs = sum(s['total_syncs'] for s in node_stats.values())
avg_times = [s['avg_sync_time'] for s in node_stats.values()]
return {
'total_syncs': total_syncs,
'avg_sync_time': sum(avg_times) / len(avg_times) if avg_times else 0,
'nodes_active': len(node_stats)
}
时间窗口统计
from collections import deque
import threading
import time
class TimeWindowMetrics:
def __init__(self, window_size=60): # 60秒窗口
self.window = deque(maxlen=window_size)
self.lock = threading.Lock()
self.current_second = {}
def record_event(self, event_type: str, value: float = 1.0):
"""记录事件"""
with self.lock:
current_time = int(time.time())
# 每秒聚合
if current_time not in self.current_second:
self.current_second[current_time] = {
'count': 0,
'total_value': 0,
'events': defaultdict(int)
}
sec_data = self.current_second[current_time]
sec_data['count'] += 1
sec_data['total_value'] += value
sec_data['events'][event_type] += 1
# 移除非当前秒数据到窗口
self.flush_old_seconds(current_time)
def flush_old_seconds(self, current_time: int):
"""将超过1秒的数据移到窗口"""
to_remove = []
for sec in self.current_second:
if sec < current_time:
self.window.append({
'timestamp': sec,
**self.current_second[sec]
})
to_remove.append(sec)
for sec in to_remove:
del self.current_second[sec]
def get_window_stats(self) -> Dict:
"""获取窗口统计"""
with self.lock:
if not self.window:
return {}
total_events = sum(w['count'] for w in self.window)
total_value = sum(w['total_value'] for w in self.window)
window_duration = len(self.window)
# 事件类型统计
event_stats = defaultdict(int)
for w in self.window:
for event_type, count in w['events'].items():
event_stats[event_type] += count
return {
'events_per_second': total_events / window_duration if window_duration else 0,
'avg_value': total_value / total_events if total_events else 0,
'event_distribution': dict(event_stats),
'window_duration_seconds': window_duration
}
完整统计脚本示例
#!/usr/bin/env python3
"""
分布式同步统计脚本
支持多种采集方式:Redis/HTTP/数据库
"""
import sys
import json
import time
from datetime import datetime
import argparse
from typing import Dict, List, Optional
class DistributedSyncStats:
def __init__(self, collector_type='redis', **kwargs):
self.collector_type = collector_type
self.collector = self.init_collector(kwargs)
self.metrics_history = []
def init_collector(self, config):
"""初始化采集器"""
if self.collector_type == 'redis':
return MessageQueueCollector(
config.get('redis_host', 'localhost'),
config.get('redis_port', 6379)
)
elif self.collector_type == 'http':
return CentralizedMetricCollector(
config.get('config_path', 'nodes.yaml')
)
else:
raise ValueError(f"Unsupported collector type: {self.collector_type}")
def collect_and_report(self):
"""采集并报告"""
print(f"\n=== 分布式同步统计报告 ===")
print(f"时间: {datetime.now().isoformat()}")
if self.collector_type == 'redis':
stats = self.collector.get_real_time_stats()
else:
metrics = asyncio.run(self.collector.collect_all_metrics())
stats = self.collector.aggregate_metrics(metrics)
self.pretty_print(stats)
return stats
def pretty_print(self, stats: Dict):
"""美化输出"""
print(f"\n📊 整体指标:")
print(f" - 总节点数: {stats.get('total_nodes', stats.get('node_count', 'N/A'))}")
print(f" - 活跃节点: {stats.get('active_nodes', stats.get('nodes_active', 'N/A'))}")
print(f" - 同步总量: {stats.get('total_sync_count', stats.get('total_syncs', 'N/A'))}")
print(f" - 成功数: {stats.get('success_count', 'N/A')}")
print(f" - 失败数: {stats.get('error_count', stats.get('sync_failed', 'N/A'))}")
print(f" - 成功率: {stats.get('sync_success_rate', 'N/A')}%")
print(f" - 平均延迟: {stats.get('avg_response_time', stats.get('avg_sync_time', 'N/A'))}ms")
print(f" - P95延迟: {stats.get('p95_latency', 'N/A')}ms")
print(f" - P99延迟: {stats.get('p99_latency', 'N/A')}ms")
print(f" - 总数据量: {stats.get('total_data_volume', 'N/A')}MB")
# 如果有节点级别详情
if 'nodes' in stats:
print(f"\n📈 节点详情:")
for node_id, node_stat in stats['nodes'].items():
print(f" [{node_id}]: sync={node_stat.get('total_syncs', 0)}, "
f"avg_time={node_stat.get('avg_sync_time', 0):.2f}ms")
def main():
parser = argparse.ArgumentParser(description='分布式同步统计工具')
parser.add_argument('--type', choices=['redis', 'http'], default='redis',
help='采集器类型')
parser.add_argument('--interval', type=int, default=10,
help='采集间隔(秒)')
parser.add_argument('--redis-host', default='localhost', help='Redis地址')
parser.add_argument('--redis-port', type=int, default=6379, help='Redis端口')
parser.add_argument('--continuous', action='store_true',
help='持续监控模式')
args = parser.parse_args()
collector_config = {
'redis_host': args.redis_host,
'redis_port': args.redis_port
}
stats = DistributedSyncStats(args.type, **collector_config)
if args.continuous:
try:
while True:
stats.collect_and_report()
print(f"\n等待 {args.interval} 秒后再次采集...")
time.sleep(args.interval)
except KeyboardInterrupt:
print("\n\n采集停止")
else:
stats.collect_and_report()
if __name__ == '__main__':
main()
最佳实践建议
- 时序数据库集成:将指标存储到 InfluxDB、Prometheus 等时序数据库
- 可视化:结合 Grafana 进行展示
- 告警机制:设置阈值,邮件/钉钉/企业微信通知
- 采样策略:根据系统规模调整采样频率和粒度
- 容错处理:当某个节点不可达时使用指数退避重试
这个方案可以根据实际需求进行调整和扩展。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播