Skip to main content

flare_core_runtime/health/
checker.rs

1//! 健康检查器实现
2//!
3//! 定期执行健康检查并在失败时触发回调
4
5use super::HealthCheck;
6use std::sync::Arc;
7use tokio::sync::RwLock;
8use tracing::{debug, error, warn};
9
10type HealthFailureCallback = Arc<dyn Fn(&str) + Send + Sync>;
11
12/// 健康检查结果
13#[derive(Debug, Clone)]
14pub struct HealthCheckResult {
15    /// 检查名称
16    pub name: String,
17    /// 是否成功
18    pub healthy: bool,
19    /// 错误信息(如果失败)
20    pub error: Option<String>,
21}
22
23/// 健康检查器
24///
25/// 定期执行健康检查并在失败时触发回调
26///
27/// # 示例
28///
29/// ```rust,ignore
30/// use flare_core_runtime::health::{HealthChecker, HealthCheck};
31/// use std::sync::Arc;
32///
33/// let mut checker = HealthChecker::new();
34/// checker.add_check(Arc::new(MyHealthCheck));
35///
36/// // 执行健康检查
37/// let results = checker.check_all().await;
38/// ```
39pub struct HealthChecker {
40    checks: Vec<Arc<dyn HealthCheck>>,
41    failure_counts: Arc<RwLock<std::collections::HashMap<String, u32>>>,
42    failure_threshold: u32,
43    on_failure: Option<HealthFailureCallback>,
44}
45
46impl HealthChecker {
47    /// 创建新的健康检查器
48    pub fn new() -> Self {
49        Self {
50            checks: Vec::new(),
51            failure_counts: Arc::new(RwLock::new(std::collections::HashMap::new())),
52            failure_threshold: 3,
53            on_failure: None,
54        }
55    }
56
57    /// 设置失败阈值
58    pub fn with_failure_threshold(mut self, threshold: u32) -> Self {
59        self.failure_threshold = threshold;
60        self
61    }
62
63    /// 设置失败回调
64    pub fn with_on_failure(mut self, callback: HealthFailureCallback) -> Self {
65        self.on_failure = Some(callback);
66        self
67    }
68
69    /// 添加健康检查
70    ///
71    /// # 参数
72    ///
73    /// * `check` - 健康检查实现
74    pub fn add_check(&mut self, check: Arc<dyn HealthCheck>) {
75        let name = check.name().to_string();
76        self.checks.push(check);
77        debug!(check_name = %name, "Health check added");
78    }
79
80    /// 执行所有健康检查
81    ///
82    /// # 返回
83    ///
84    /// 返回所有检查的结果列表
85    pub async fn check_all(&self) -> Vec<HealthCheckResult> {
86        let mut results = Vec::new();
87
88        for check in &self.checks {
89            let result = self.check_one(check.as_ref()).await;
90            results.push(result);
91        }
92
93        results
94    }
95
96    /// 执行单个健康检查
97    async fn check_one(&self, check: &dyn HealthCheck) -> HealthCheckResult {
98        let name = check.name().to_string();
99
100        match check.check().await {
101            Ok(_) => {
102                // 重置失败计数
103                let mut counts = self.failure_counts.write().await;
104                counts.insert(name.clone(), 0);
105
106                HealthCheckResult {
107                    name: name.clone(),
108                    healthy: true,
109                    error: None,
110                }
111            }
112            Err(e) => {
113                // 增加失败计数
114                let mut counts = self.failure_counts.write().await;
115                let count = counts.entry(name.clone()).or_insert(0);
116                *count += 1;
117
118                error!(
119                    check_name = %name,
120                    failure_count = *count,
121                    threshold = self.failure_threshold,
122                    error = %e,
123                    "Health check failed"
124                );
125
126                // 检查是否超过阈值
127                if *count >= self.failure_threshold {
128                    warn!(
129                        check_name = %name,
130                        "Health check failure threshold exceeded"
131                    );
132
133                    // 触发回调
134                    if let Some(callback) = &self.on_failure {
135                        callback(&name);
136                    }
137                }
138
139                HealthCheckResult {
140                    name: name.clone(),
141                    healthy: false,
142                    error: Some(e.to_string()),
143                }
144            }
145        }
146    }
147
148    /// 检查是否所有检查都健康
149    pub async fn is_healthy(&self) -> bool {
150        let results = self.check_all().await;
151        results.iter().all(|r| r.healthy)
152    }
153
154    /// 获取检查数量
155    pub fn check_count(&self) -> usize {
156        self.checks.len()
157    }
158}
159
160impl Default for HealthChecker {
161    fn default() -> Self {
162        Self::new()
163    }
164}
165
166#[cfg(test)]
167mod tests {
168    use super::*;
169    use crate::error::HealthError;
170    use std::future::Future;
171    use std::pin::Pin;
172
173    struct TestHealthCheck {
174        name: String,
175        healthy: bool,
176    }
177
178    impl HealthCheck for TestHealthCheck {
179        fn check(&self) -> Pin<Box<dyn Future<Output = Result<(), HealthError>> + Send + '_>> {
180            let healthy = self.healthy;
181            let name = self.name.clone();
182            Box::pin(async move {
183                if healthy {
184                    Ok(())
185                } else {
186                    Err(HealthError::CheckFailed {
187                        name,
188                        reason: "Test failure".to_string(),
189                    })
190                }
191            })
192        }
193
194        fn name(&self) -> &str {
195            &self.name
196        }
197    }
198
199    #[tokio::test]
200    async fn test_health_checker_add() {
201        let mut checker = HealthChecker::new();
202        checker.add_check(Arc::new(TestHealthCheck {
203            name: "test-check".to_string(),
204            healthy: true,
205        }));
206
207        assert_eq!(checker.check_count(), 1);
208    }
209
210    #[tokio::test]
211    async fn test_health_checker_healthy() {
212        let mut checker = HealthChecker::new();
213        checker.add_check(Arc::new(TestHealthCheck {
214            name: "test-check".to_string(),
215            healthy: true,
216        }));
217
218        let is_healthy = checker.is_healthy().await;
219        assert!(is_healthy);
220    }
221
222    #[tokio::test]
223    async fn test_health_checker_unhealthy() {
224        let mut checker = HealthChecker::new();
225        checker.add_check(Arc::new(TestHealthCheck {
226            name: "test-check".to_string(),
227            healthy: false,
228        }));
229
230        let is_healthy = checker.is_healthy().await;
231        assert!(!is_healthy);
232    }
233}