use std::collections::{BTreeMap, HashMap};
use serde::{Deserialize, Serialize};
use std::hash::Hash;
use tokio::time::{Duration, Instant};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ResourceType {
Cpu,
Memory,
Storage,
Network,
Gpu,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ResourceRequest {
pub resource_type: ResourceType,
pub amount: f64,
pub priority: u8,
pub workload_id: String,
pub timestamp: Instant,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Workload {
pub id: String,
pub name: String,
pub resource_requirements: Vec<ResourceRequest>,
pub allocated_resources: HashMap<ResourceType, f64>,
pub performance_requirements: HashMap<String, f64>,
pub importance: u8,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ResourceUsage {
pub resource_type: ResourceType,
pub usage: f64,
pub capacity: f64,
pub utilization_rate: f64,
pub timestamp: Instant,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Cluster {
pub id: String,
pub name: String,
pub total_resources: HashMap<ResourceType, f64>,
pub current_usage: HashMap<ResourceType, f64>,
pub workloads: Vec<Workload>,
pub health_score: f64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum AllocationStrategy {
PerformanceFirst,
CostFirst,
Balanced,
Emergency,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AllocationPlan {
pub workload_id: String,
pub allocations: HashMap<ResourceType, f64>,
pub expected_improvement: f64,
pub confidence: f64,
pub strategy: AllocationStrategy,
pub created_at: Instant,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RebalanceResult {
pub success: bool,
pub rebalanced_allocations: Vec<AllocationPlan>,
pub expected_improvement: f64,
pub workloads_adjusted: usize,
pub message: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ResourceForecast {
pub predicted_demand: HashMap<ResourceType, f64>,
pub forecast_horizon_minutes: u64,
pub confidence_interval: f64,
pub generated_at: Instant,
}
#[derive(Debug, Clone)]
pub struct ResourceOptimizer {
usage_history: Vec<ResourceUsage>,
config: OptimizerConfig,
}
#[derive(Debug, Clone)]
pub struct OptimizerConfig {
pub prediction_window_hours: u64,
pub rebalance_threshold: f64,
pub min_improvement_threshold: f64,
pub max_adjustment_step: f64,
}
impl Default for OptimizerConfig {
fn default() -> Self {
Self {
prediction_window_hours: 24,
rebalance_threshold: 20.0,
min_improvement_threshold: 5.0,
max_adjustment_step: 0.2,
}
}
}
impl ResourceOptimizer {
pub fn new(config: OptimizerConfig) -> Self {
Self {
usage_history: Vec::new(),
config,
}
}
pub fn new_with_defaults() -> Self {
Self::new(OptimizerConfig::default())
}
pub async fn allocate_resources(&self, workload: &Workload) -> AllocationPlan {
let mut allocations = HashMap::new();
for req in &workload.resource_requirements {
let optimal_amount: _ = self.calculate_optimal_allocation(req, workload);
allocations.insert(req.resource_type.clone(), optimal_amount);
}
let expected_improvement: _ = self.calculate_expected_improvement(workload, &allocations);
let confidence: _ = self.calculate_confidence(&allocations);
AllocationPlan {
workload_id: workload.id.clone(),
allocations,
expected_improvement,
confidence,
strategy: AllocationStrategy::Balanced,
created_at: Instant::now(),
}
}
pub async fn rebalance_resources(&self, cluster: &Cluster) -> RebalanceResult {
let mut rebalanced_allocations = Vec::new();
let mut workloads_adjusted = 0;
let mut total_improvement = 0.0;
let utilization: _ = self.calculate_cluster_utilization(cluster);
let workloads_to_rebalance: _ = self.identify_workloads_to_rebalance(cluster, &utilization);
for workload in &workloads_to_rebalance {
let plan: _ = self.allocate_resources(workload).await;
rebalanced_allocations.push(plan.clone());
workloads_adjusted += 1;
total_improvement += plan.expected_improvement;
}
let avg_improvement: _ = if workloads_adjusted > 0 {
total_improvement / workloads_adjusted as f64
} else {
0.0
};
RebalanceResult {
success: workloads_adjusted > 0,
rebalanced_allocations,
expected_improvement: avg_improvement,
workloads_adjusted,
message: if workloads_adjusted > 0 {
format!("成功重新平衡 {} 个工作负载", workloads_adjusted)
} else {
"当前资源分配已经最优,无需重新平衡".to_string()
},
}
}
pub async fn predict_resource_needs(&self, history: &[ResourceUsage]) -> ResourceForecast {
let mut predicted_demand = HashMap::new();
let mut usage_by_type: HashMap<ResourceType, Vec<&ResourceUsage>> = HashMap::new();
for usage in history {
usage_by_type
.entry(usage.resource_type.clone())
.or_insert_with(Vec::new)
.push(usage);
}
for (resource_type, usage_list) in usage_by_type {
let predicted: _ = self.predict_single_resource_demand(&usage_list);
predicted_demand.insert(resource_type, predicted);
}
ResourceForecast {
predicted_demand,
forecast_horizon_minutes: self.config.prediction_window_hours * 60,
confidence_interval: 0.85,
generated_at: Instant::now(),
}
}
fn calculate_optimal_allocation(&self, req: &ResourceRequest, workload: &Workload) -> f64 {
let current_utilization: _ = self.get_current_utilization(&req.resource_type);
let adjustment_factor: _ = if current_utilization > 80.0 {
1.1
} else if current_utilization < 50.0 {
0.9
} else {
1.0
};
let base_allocation: _ = req.amount;
let priority_factor: _ = 1.0 + (req.priority as f64 / 100.0) * 0.5;
(base_allocation * adjustment_factor * priority_factor).max(0.0)
}
fn calculate_expected_improvement(
&self,
workload: &Workload,
allocations: &HashMap<ResourceType, f64>,
) -> f64 {
let mut total_score = 0.0;
for (resource_type, allocated) in allocations {
let requirement: _ = workload
.resource_requirements
.iter()
.find(|r| r.resource_type == *resource_type);
if let Some(req) = requirement {
let allocation_ratio: _ = if req.amount > 0.0 {
(allocated / req.amount).min(2.0)
} else {
1.0
};
let priority_bonus: _ = req.priority as f64 / 100.0 * 10.0;
total_score += allocation_ratio * 20.0 + priority_bonus;
}
}
(total_score / workload.resource_requirements.len() as f64).min(50.0)
}
fn calculate_confidence(&self, allocations: &HashMap<ResourceType, f64>) -> f64 {
let mut confidence = 1.0;
for (_resource_type, amount) in allocations {
if *amount <= 0.0 {
confidence -= 0.3;
} else if *amount > 1000.0 {
confidence -= 0.1;
}
}
confidence.max(0.5).min(0.95)
}
fn calculate_cluster_utilization(&self, cluster: &Cluster) -> HashMap<ResourceType, f64> {
let mut utilization = HashMap::new();
for (resource_type, &total) in &cluster.total_resources {
let used: _ = cluster
.current_usage
.get(resource_type)
.copied()
.unwrap_or(0.0);
let utilization_rate: _ = if total > 0.0 {
(used / total) * 100.0
} else {
0.0
};
utilization.insert(resource_type.clone(), utilization_rate);
}
utilization
}
fn identify_workloads_to_rebalance(
&self,
cluster: &Cluster,
utilization: &HashMap<ResourceType, f64>,
) -> Vec<Workload> {
let mut workloads_to_rebalance = Vec::new();
for workload in &cluster.workloads {
let mut needs_rebalance = false;
for req in &workload.resource_requirements {
let current_util: _ = utilization.get(&req.resource_type);
if let Some(&util_rate) = current_util {
if util_rate > 90.0 || util_rate < 30.0 {
needs_rebalance = true;
break;
}
}
}
if needs_rebalance {
workloads_to_rebalance.push(workload.clone());
}
}
workloads_to_rebalance
}
fn get_current_utilization(&self, resource_type: &ResourceType) -> f64 {
if let Some(latest_usage) = self
.usage_history
.iter()
.rev()
.find(|u| u.resource_type == *resource_type)
{
latest_usage.utilization_rate
} else {
50.0 }
}
fn predict_single_resource_demand(&self, usage_list: &[&ResourceUsage]) -> f64 {
if usage_list.is_empty() {
return 0.0;
}
let window_size: _ = usage_list.len().min(10);
let recent_usage: Vec<f64> = usage_list
.iter()
.rev()
.take(window_size)
.map(|u| u.usage)
.collect();
let sum: f64 = recent_usage.iter().sum();
sum / recent_usage.len() as f64
}
pub fn add_usage_history(&mut self, usage: ResourceUsage) {
self.usage_history.push(usage);
if self.usage_history.len() > 10000 {
self.usage_history.drain(0..1000);
}
}
pub fn get_optimization_stats(&self) -> OptimizationStats {
OptimizationStats {
total_allocations: self.usage_history.len(),
avg_utilization: self.calculate_average_utilization(),
predicted_adjustments: self.usage_history.len() / 100,
}
}
fn calculate_average_utilization(&self) -> f64 {
if self.usage_history.is_empty() {
return 0.0;
}
let sum: f64 = self.usage_history.iter().map(|u| u.utilization_rate).sum();
sum / self.usage_history.len() as f64
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct OptimizationStats {
pub total_allocations: usize,
pub avg_utilization: f64,
pub predicted_adjustments: usize,
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::{Duration, Instant};
#[tokio::test]
async fn test_allocate_resources_basic() {
let optimizer: _ = ResourceOptimizer::new_with_defaults();
let workload: _ = Workload {
id: "test-1".to_string(),
name: "Test Workload".to_string(),
resource_requirements: vec![ResourceRequest {
resource_type: ResourceType::Cpu,
amount: 100.0,
priority: 80,
workload_id: "test-1".to_string(),
timestamp: Instant::now(),
}],
allocated_resources: HashMap::new(),
performance_requirements: HashMap::new(),
importance: 8,
};
let plan: _ = optimizer.allocate_resources(&workload).await;
assert_eq!(plan.workload_id, "test-1");
assert!(plan.expected_improvement > 0.0);
assert!(plan.confidence > 0.0);
assert!(plan.confidence <= 1.0);
}
#[tokio::test]
async fn test_rebalance_resources() {
let optimizer: _ = ResourceOptimizer::new_with_defaults();
let mut cluster = Cluster {
id: "cluster-1".to_string(),
name: "Test Cluster".to_string(),
total_resources: {
let mut map = HashMap::new();
map.insert(ResourceType::Cpu, 1000.0);
map.insert(ResourceType::Memory, 8192.0);
map
},
current_usage: {
let mut map = HashMap::new();
map.insert(ResourceType::Cpu, 500.0);
map.insert(ResourceType::Memory, 4000.0);
map
},
workloads: vec![],
health_score: 80.0,
};
let result: _ = optimizer.rebalance_resources(&cluster).await;
assert!(result.workloads_adjusted >= 0);
}
#[tokio::test]
async fn test_predict_resource_needs() {
let optimizer: _ = ResourceOptimizer::new_with_defaults();
let history: _ = vec![
ResourceUsage {
resource_type: ResourceType::Cpu,
usage: 100.0,
capacity: 1000.0,
utilization_rate: 10.0,
timestamp: Instant::now(),
},
ResourceUsage {
resource_type: ResourceType::Cpu,
usage: 200.0,
capacity: 1000.0,
utilization_rate: 20.0,
timestamp: Instant::now(),
},
];
let forecast: _ = optimizer.predict_resource_needs(&history).await;
assert!(!forecast.predicted_demand.is_empty());
assert!(forecast.predicted_demand.contains_key(&ResourceType::Cpu));
}
}