Async Healthcheck Multiple Endpoints
Intro
Today, I'll show you how to use tokio to do healthcheck for multiple endpoints.
The architecture is simple:
- Initialized vector of healthcheck endpoints
- Spawn futures to do health check
Code
// use tokio::time::sleep; use serde::{Deserialize, Serialize}; use std::fs::File; use std::io::BufReader; use std::time::Duration; use tokio::select; use tokio::sync::mpsc; use tokio::time; use tokio::time::{interval, sleep}; #[derive(Debug, Deserialize, Serialize, Clone)] struct Config { interval: u64, url: String, } async fn check_url(config: Config) { loop { println!("In check_url loop"); let url = &config.url; match reqwest::get(url).await { Err(e) => println!("Error: Failed to access {}: {}", config.url, e), Ok(response) => { // println!("{response:?}"); if !response.status().is_success() { println!( "Error: {} returned status code {}", config.url, response.status() ); } println!("check for {url} OK"); } } sleep(Duration::from_secs(config.interval)).await; } } #[tokio::main] async fn main() { // Load configuration from file // let file = File::open("config.json").expect("Failed to open config file"); // let reader = BufReader::new(file); // let configs: Vec<Config> = // serde_json::from_reader(reader).expect("Failed to parse config file"); let configs = vec![ Config { interval: 10, url: "http://www.baidu.com".to_string() }, Config { interval: 10, url: "http://www.qq.com".to_string() }, ]; // Create a shared timer // let mut ticker = interval(Duration::from_secs(1)); // let mut interval = // time::interval(time::Duration::from_millis(consume_interval)); // Create a task for each URL and spawn it // // NOTE: we don't need to run in loop in spawn, check_url already has loop // for config in configs { // // let mut tick = ticker.tick(); // tokio::spawn(async move { // let mut ticker = interval(Duration::from_secs(1)); // loop { // select! { // // _ = tick => { // _ = ticker.tick() => { // println!("1s ..."); // check_url(config.clone()).await; // } // } // } // }); // } for config in configs { tokio::spawn(async move { println!("spawn check future ..."); check_url(config.clone()).await; }); } println!("Infinite loop"); // Keep the program running so that other tasks can continue to run time::sleep(Duration::from_secs(2000)).await; // loop {} }
Code explain
- Load configuration from file or hard code the configuration
We can hard code the configuration or load configuration from file.
#![allow(unused)] fn main() { // let file = File::open("config.json").expect("Failed to open config file"); // let reader = BufReader::new(file); // let configs: Vec<Config> = // serde_json::from_reader(reader).expect("Failed to parse config file"); let configs = vec![ Config { interval: 10, url: "http://www.baidu.com".to_string() }, Config { interval: 10, url: "http://www.qq.com".to_string() }, ]; }
- Create a task for each URL and spawn it
#![allow(unused)] fn main() { for config in configs { tokio::spawn(async move { println!("spawn check future ..."); check_url(config.clone()).await; }); } }
- Keep the program running so that other tasks can continue to run
#![allow(unused)] fn main() { time::sleep(Duration::from_secs(2000)).await; // loop {} // Keep the program running so that other tasks can continue to run }