use crate::aiops::anomaly_detection::{Anomaly, AnomalyDetector, BaselineCalculator};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Duration, SystemTime};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum MetricType {
CpuUsage,
MemoryUsage,
DiskUsage,
NetworkLatency,
ErrorRate,
Throughput,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemMetric {
pub metric_type: MetricType,
pub value: f64,
pub timestamp: SystemTime,
pub labels: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TimeSeriesData {
pub timestamp: SystemTime,
pub value: f64,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum TrendDirection {
Increasing,
Decreasing,
Stable,
Volatile,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrendReport {
pub direction: TrendDirection,
pub slope: f64,
pub confidence: f64,
pub predicted_values: Vec<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Prediction {
pub metric_type: MetricType,
pub probability: f64,
pub predicted_time: SystemTime,
pub severity: PredictionSeverity,
pub affected_services: Vec<String>,
pub confidence_score: f64,
pub recommendations: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum PredictionSeverity {
Low,
Medium,
High,
Critical,
}
#[derive(Debug)]
pub struct TrendAnalyzer {
baseline_calculator: Arc<BaselineCalculator>,
}
impl TrendAnalyzer {
pub fn new() -> Self {
Self {
baseline_calculator: Arc::new(Mutex::new(BaselineCalculator::new())),
}
}
pub async fn analyze_trends(
&self,
time_series: &[TimeSeriesData],
) -> Result<TrendReport, Box<dyn std::error::Error>> {
if time_series.len() < 2 {
return Err("时间序列数据点不足".into());
}
let (slope, intercept) = self.calculate_linear_regression(time_series)?;
let confidence: _ = self.calculate_confidence(time_series, slope)?;
let direction: _ = self.determine_direction(slope, confidence)?;
let predicted_values: _ = self.predict_future_values(time_series, slope, intercept)?;
Ok(TrendReport {
direction,
slope,
confidence,
predicted_values,
})
}
fn calculate_linear_regression(
&self,
time_series: &[TimeSeriesData],
) -> Result<(f64, f64), Box<dyn std::error::Error>> {
let n: _ = time_series.len() as f64;
let mut sum_x = 0.0;
let mut sum_y = 0.0;
let mut sum_xy = 0.0;
let mut sum_x2 = 0.0;
for (i, data) in time_series.iter().enumerate() {
let x: _ = i as f64;
let y: _ = data.value;
sum_x += x;
sum_y += y;
sum_xy += x * y;
sum_x2 += x * x;
}
let slope: _ = (n * sum_xy - sum_x * sum_y) / (n * sum_x2 - sum_x * sum_x);
let intercept: _ = (sum_y - slope * sum_x) / n;
Ok((slope, intercept))
}
fn calculate_confidence(
&self,
time_series: &[TimeSeriesData],
slope: f64,
) -> Result<f64, Box<dyn std::error::Error>> {
let mean: f64 = time_series.iter().map(|d| d.value).sum::<f64>() / time_series.len() as f64;
let variance: f64 = time_series
.iter()
.map(|d| (d.value - mean).powi(2))
.sum::<f64>()
/ time_series.len() as f64;
let confidence: _ = 1.0 / (1.0 + variance);
Ok(confidence.min(1.0).max(0.0))
}
fn determine_direction(
&self,
slope: f64,
confidence: f64,
) -> Result<TrendDirection, Box<dyn std::error::Error>> {
let threshold: _ = 0.1;
if confidence < 0.5 {
return Ok(TrendDirection::Volatile);
}
if slope.abs() < threshold {
Ok(TrendDirection::Stable)
} else if slope > 0.0 {
Ok(TrendDirection::Increasing)
} else {
Ok(TrendDirection::Decreasing)
}
}
fn predict_future_values(
&self,
time_series: &[TimeSeriesData],
slope: f64,
intercept: f64,
) -> Result<Vec<f64>, Box<dyn std::error::Error>> {
let n: _ = time_series.len();
let mut predictions = Vec::new();
for i in n..n + 5 {
let predicted_value: _ = slope * i as f64 + intercept;
predictions.push(predicted_value);
}
Ok(predictions)
}
}
impl Default for TrendAnalyzer {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct ModelTrainer {
historical_data: Arc<RwLock<Vec<SystemMetric>>>,
}
impl ModelTrainer {
pub fn new() -> Self {
Self {
historical_data: Arc::new(Mutex::new(Vec::new())),
}
}
pub async fn train_model(
&self,
metrics: &[SystemMetric],
) -> Result<(), Box<dyn std::error::Error>> {
let mut data = self.historical_data.write().await;
data.extend_from_slice(metrics);
Ok(())
}
pub async fn calculate_failure_probability(
&self,
metrics: &[SystemMetric],
) -> Result<HashMap<MetricType, f64>, Box<dyn std::error::Error>> {
let mut probabilities = HashMap::new();
for metric in metrics {
let probability: _ = match metric.metric_type {
MetricType::CpuUsage => self.calculate_cpu_failure_probability(metric.value),
MetricType::MemoryUsage => self.calculate_memory_failure_probability(metric.value),
MetricType::DiskUsage => self.calculate_disk_failure_probability(metric.value),
MetricType::NetworkLatency => {
self.calculate_network_failure_probability(metric.value)
}
MetricType::ErrorRate => {
self.calculate_error_rate_failure_probability(metric.value)
}
MetricType::Throughput => {
self.calculate_throughput_failure_probability(metric.value)
}
};
probabilities.insert(metric.metric_type, probability);
}
Ok(probabilities)
}
fn calculate_cpu_failure_probability(&self, cpu_usage: f64) -> f64 {
if cpu_usage < 50.0 {
0.1
} else if cpu_usage < 70.0 {
0.3
} else if cpu_usage < 85.0 {
0.6
} else if cpu_usage < 95.0 {
0.85
} else {
0.95
}
}
fn calculate_memory_failure_probability(&self, memory_usage: f64) -> f64 {
if memory_usage < 60.0 {
0.1
} else if memory_usage < 75.0 {
0.4
} else if memory_usage < 88.0 {
0.7
} else if memory_usage < 96.0 {
0.9
} else {
0.98
}
}
fn calculate_disk_failure_probability(&self, disk_usage: f64) -> f64 {
if disk_usage < 70.0 {
0.05
} else if disk_usage < 85.0 {
0.3
} else if disk_usage < 95.0 {
0.8
} else {
0.95
}
}
fn calculate_network_failure_probability(&self, latency: f64) -> f64 {
if latency < 50.0 {
0.1
} else if latency < 100.0 {
0.3
} else if latency < 200.0 {
0.6
} else if latency < 500.0 {
0.85
} else {
0.95
}
}
fn calculate_error_rate_failure_probability(&self, error_rate: f64) -> f64 {
if error_rate < 0.01 {
0.05
} else if error_rate < 0.05 {
0.4
} else if error_rate < 0.1 {
0.7
} else if error_rate < 0.2 {
0.9
} else {
0.99
}
}
fn calculate_throughput_failure_probability(&self, throughput: f64) -> f64 {
if throughput > 800.0 {
0.1
} else if throughput > 500.0 {
0.4
} else if throughput > 200.0 {
0.7
} else if throughput > 100.0 {
0.9
} else {
0.98
}
}
}
impl Default for ModelTrainer {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct PredictionEngine {
anomaly_detector: Arc<AnomalyDetector>,
trend_analyzer: Arc<TrendAnalyzer>,
model_trainer: Arc<ModelTrainer>,
}
impl PredictionEngine {
pub fn new() -> Self {
Self {
anomaly_detector: Arc::new(AnomalyDetector::new()),
trend_analyzer: Arc::new(TrendAnalyzer::new()),
model_trainer: Arc::new(ModelTrainer::new()),
}
}
pub async fn predict_failures(
&self,
metrics: &[SystemMetric],
) -> Result<Vec<Prediction>, Box<dyn std::error::Error>> {
if metrics.is_empty() {
return Err("指标数据为空".into());
}
let mut predictions = Vec::new();
let metrics_by_type: _ = self.group_metrics_by_type(metrics);
for (metric_type, type_metrics) in metrics_by_type {
let values: Vec<f64> = type_metrics.iter().map(|m| m.value).collect();
let anomalies: _ = self.anomaly_detector.detect_anomalies(&values).await?;
let time_series: _ = self.convert_to_time_series(&type_metrics);
let trend_report: _ = self.trend_analyzer.analyze_trends(&time_series).await?;
let probabilities: _ = self
.model_trainer
.calculate_failure_probability(&type_metrics)
.await?;
let probability: _ = probabilities.get(&metric_type).unwrap_or(&0.0);
if *probability > 0.5 {
let prediction: _ =
self.create_prediction(metric_type, *probability, &anomalies, &trend_report)?;
predictions.push(prediction);
}
}
Ok(predictions)
}
pub async fn analyze_trends(
&self,
historical_data: &[TimeSeriesData],
) -> Result<TrendReport, Box<dyn std::error::Error>> {
self.trend_analyzer.analyze_trends(historical_data).await
}
pub async fn calculate_failure_probability(
&self,
system_metrics: &[SystemMetric],
) -> Result<f64, Box<dyn std::error::Error>> {
let probabilities: _ = self
.model_trainer
.calculate_failure_probability(system_metrics)
.await?;
let max_probability: _ =
probabilities
.values()
.fold(0.0, |max, &prob| if prob > max { prob } else { max });
Ok(max_probability)
}
fn group_metrics_by_type(
&self,
metrics: &[SystemMetric],
) -> HashMap<MetricType, Vec<SystemMetric>> {
let mut grouped = HashMap::new();
for metric in metrics {
grouped
.entry(metric.metric_type)
.or_insert_with(Vec::new)
.push(metric.clone());
}
grouped
}
fn convert_to_time_series(&self, metrics: &[SystemMetric]) -> Vec<TimeSeriesData> {
metrics
.iter()
.map(|m| TimeSeriesData {
timestamp: m.timestamp,
value: m.value,
})
.collect()
}
fn create_prediction(
&self,
metric_type: MetricType,
probability: f64,
anomalies: &[Anomaly],
trend_report: &TrendReport,
) -> Result<Prediction, Box<dyn std::error::Error>> {
let severity: _ = if probability > 0.9 {
PredictionSeverity::Critical
} else if probability > 0.7 {
PredictionSeverity::High
} else if probability > 0.5 {
PredictionSeverity::Medium
} else {
PredictionSeverity::Low
};
let affected_services: _ = self.identify_affected_services(metric_type);
let recommendations: _ =
self.generate_recommendations(metric_type, probability, anomalies, trend_report)?;
Ok(Prediction {
metric_type,
probability,
predicted_time: SystemTime::now() + Duration::from_secs(3600), severity,
affected_services,
confidence_score: trend_report.confidence,
recommendations,
})
}
fn identify_affected_services(&self, metric_type: MetricType) -> Vec<String> {
match metric_type {
MetricType::CpuUsage | MetricType::MemoryUsage => {
vec!["api-service".to_string(), "worker-service".to_string()]
}
MetricType::DiskUsage => {
vec!["storage-service".to_string()]
}
MetricType::NetworkLatency => {
vec!["gateway".to_string(), "load-balancer".to_string()]
}
MetricType::ErrorRate | MetricType::Throughput => {
vec!["api-service".to_string()]
}
}
}
fn generate_recommendations(
&self,
metric_type: MetricType,
probability: f64,
anomalies: &[Anomaly],
trend_report: &TrendReport,
) -> Result<Vec<String>, Box<dyn std::error::Error>> {
let mut recommendations = Vec::new();
match metric_type {
MetricType::CpuUsage => {
if probability > 0.8 {
recommendations.push("立即扩容 CPU 资源".to_string());
recommendations.push("检查 CPU 密集型进程".to_string());
}
recommendations.push("优化算法和代码".to_string());
}
MetricType::MemoryUsage => {
if probability > 0.8 {
recommendations.push("增加内存配额".to_string());
recommendations.push("重启内存泄漏服务".to_string());
}
recommendations.push("优化内存使用".to_string());
}
MetricType::DiskUsage => {
if probability > 0.8 {
recommendations.push("清理临时文件".to_string());
recommendations.push("增加磁盘空间".to_string());
}
recommendations.push("压缩日志文件".to_string());
}
MetricType::NetworkLatency => {
if probability > 0.8 {
recommendations.push("检查网络连接".to_string());
recommendations.push("优化网络配置".to_string());
}
recommendations.push("启用网络缓存".to_string());
}
MetricType::ErrorRate => {
if probability > 0.8 {
recommendations.push("立即检查错误日志".to_string());
recommendations.push("回滚最近的变更".to_string());
}
recommendations.push("加强错误处理".to_string());
}
MetricType::Throughput => {
if probability > 0.8 {
recommendations.push("扩容实例数量".to_string());
recommendations.push("优化数据库查询".to_string());
}
recommendations.push("启用负载均衡".to_string());
}
}
match trend_report.direction {
TrendDirection::Increasing => {
recommendations.push("趋势显示问题在恶化,建议立即行动".to_string());
}
TrendDirection::Volatile => {
recommendations.push("指标波动较大,建议稳定性分析".to_string());
}
_ => {}
}
Ok(recommendations)
}
}
impl Default for PredictionEngine {
fn default() -> Self {
Self::new()
}
}