23.5.2.5. 处理速率受限 API

reqwest-badge hyper-badge cat-net-badge

此实例使用 GitHub API - 速率限制展示如何处理远程服务器错误。本实例使用 hyper::header! 宏来解析响应头并检查 reqwest::StatusCode::Forbidden。如果响应超过速率限制,则将等待并重试。

use error_chain::error_chain;

use std::time::{Duration, UNIX_EPOCH};
use std::thread;
use reqwest::StatusCode;

error_chain! {
   foreign_links {
       Io(std::io::Error);
       Time(std::time::SystemTimeError);
       Reqwest(reqwest::Error);
   }
}

header! { (XRateLimitLimit, "X-RateLimit-Limit") => [usize] }
header! { (XRateLimitRemaining, "X-RateLimit-Remaining") => [usize] }
header! { (XRateLimitReset, "X-RateLimit-Reset") => [u64] }

fn main() -> Result<()> {
    loop {
        let url = "https://api.github.com/users/rust-lang-nursery ";
        let client = reqwest::Client::new();
        let response = client.get(url).send()?;

        let rate_limit = response.headers().get::<XRateLimitLimit>().ok_or(
            "response doesn't include the expected X-RateLimit-Limit header",
        )?;

        let rate_remaining = response.headers().get::<XRateLimitRemaining>().ok_or(
            "response doesn't include the expected X-RateLimit-Remaining header",
        )?;

        let rate_reset_at = response.headers().get::<XRateLimitReset>().ok_or(
            "response doesn't include the expected X-RateLimit-Reset header",
        )?;

        let rate_reset_within = Duration::from_secs(**rate_reset_at) - UNIX_EPOCH.elapsed()?;

        if response.status() == StatusCode::Forbidden && **rate_remaining == 0 {
            println!("Sleeping for {} seconds.", rate_reset_within.as_secs());
            thread::sleep(rate_reset_within);
            return main();
        } else {
            println!(
                "Rate limit is currently {}/{}, the reset of this limit will be within {} seconds.",
                **rate_remaining,
                **rate_limit,
                rate_reset_within.as_secs(),
            );
            break;
        }
    }
    Ok(())
}