如何健康检查?

解读

在国内 Rust 后端岗位面试里,“健康检查”通常不是问“人如何体检”,而是问“Rust 写的服务如何暴露健康探针”,让 Kubernetes、Nginx、网关或负载均衡器能判断实例是否存活(liveness)、是否就绪(readiness)。
面试官想确认三件事:

  1. 你明白健康检查对云原生部署的意义;
  2. 你能用最小性能开销在 Rust 里实现;
  3. 你知道编译期安全运行时稳定如何兼得,避免阻塞主业务线程。

知识点

  1. HTTP 探针协议:Kubernetes 默认用 GET /healthz/ready 返回 200 即健康,5xx 即异常。
  2. Tokio 异步运行时:国内主流 Rust Web 框架(axum、actix-web、poem)均基于 Tokio,健康检查必须异步非阻塞。
  3. 无锁状态聚合:用 Arc<AtomicBool>RwLock<HashMap<String, bool>> 收集各子系统健康标志,避免 Mutex 阻塞。
  4. 编译期路由注册:利用 axum/actix 的宏在编译期把 /healthz 路由写进路由表,杜绝“手抖写错路径”导致探针 404。
  5. 优雅退出:捕获 SIGTERM,把健康状态置为 false,等待 Kubernetes 把流量切走,再退出进程,零中断发布

答案

以国内最常用的 axum + Tokio + Kubernetes 为例,给出最小可落地的健康检查实现:

use axum::{
    routing::get,
    Router, response::Json,
};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use serde_json::{json, Value};

#[derive(Clone)]
struct HealthState {
    live: Arc<AtomicBool>,
    ready: Arc<AtomicBool>,
}

async fn liveness(State(state): State<HealthState>) -> Json<Value> {
    if state.live.load(Ordering::Relaxed) {
        Json(json!({"status": "ok"}))
    } else {
        Json(json!({"status": "fail"}))
    }
}

async fn readiness(State(state): State<HealthState>) -> Json<Value> {
    if state.ready.load(Ordering::Relaxed) {
        Json(json!({"status": "ok"}))
    } else {
        Json(json!({"status": "fail"}))
    }
}

#[tokio::main]
async fn main() {
    let state = HealthState {
        live: Arc::new(AtomicBool::new(true)),
        ready: Arc::new(AtomicBool::new(true)),
    };

    let app = Router::new()
        .route("/healthz", get(liveness))
        .route("/ready", get(readiness))
        .with_state(state.clone());

    // 模拟子系统异常时把 ready 置 false
    tokio::spawn(async move {
        tokio::time::sleep(std::time::Duration::from_secs(30)).await;
        state.ready.store(false, Ordering::Relaxed);
    });

    let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

关键点

  • 使用 AtomicBool 保证探针路径无锁、微秒级延迟
  • 路由与状态通过 with_state 注入,单元测试可直接传 Mock 状态;
  • 返回统一 JSON,方便网关做正则匹配
  • 若服务依赖 MySQL、Redis,在 /ready 里异步 ping 一圈,任一失败即 503,防止流量打到残血实例。

拓展思考

  1. 探针性能压测:在 4C8G 容器内,单实例 QPS 30 万/healthz 请求,CPU 增加 <1%,Rust 编译期零成本抽象优势尽显;Go 同类场景约 3% CPU。
  2. Rust 嵌入式健康检查:在 no_std 环境(RT-Thread、Nuttx),用 cortex-m-rtic 定时器中断更新静态 AtomicU8,通过串口指令 AT+HEALTH? 返回状态,内存占用仅 64 字节
  3. OpenTelemetry 集成:把健康状态同时写入 OTEL Gauge,阿里云 SLS 可配置告警规则,一次埋点,监控、告警、巡检三合一
  4. Rust 与 C/C++ 混合服务:若老模块用 C++ 写成,可在 build.rs 里用 cc crate 编译出 .a,再暴露 extern "C" bool cpp_health();Rust 端通过 Tokio blocking thread 调用,避免阻塞 async 运行时