12 个版本 (5 个破坏性更新)

0.6.0 2021 年 12 月 2 日
0.5.0 2021 年 10 月 26 日
0.4.3 2021 年 10 月 12 日
0.4.0 2021 年 9 月 26 日
0.1.1 2021 年 9 月 26 日

#1222异步

Download history 97/week @ 2024-04-01 25/week @ 2024-04-08 20/week @ 2024-04-15 34/week @ 2024-04-22 24/week @ 2024-04-29 19/week @ 2024-05-06 40/week @ 2024-05-13 43/week @ 2024-05-20 42/week @ 2024-05-27 19/week @ 2024-06-03 14/week @ 2024-06-10 34/week @ 2024-06-17 5/week @ 2024-06-24 66/week @ 2024-07-01 29/week @ 2024-07-08 53/week @ 2024-07-15

153 每月下载量
2 crates 中使用

MIT 许可

47KB
892

Crates.io Docs.rs Workflow Status

fure

一个用于重试 futures 的 crate。

Policy trait 将帮助您定义不同的重试策略。

一些内置策略可以在 policies 模块中找到。

默认情况下,此 crate 使用 tokio 定时器来实现 crate::policies::intervalcrate::policies::backoff 策略,但也可以作为功能 async-std 使用。

示例。

间隔重试。

开始发送请求,设置 1 秒定时器,并等待其中之一。

如果定时器先完成(这意味着请求在 1 秒内没有完成),则再发送一个请求。

如果请求先完成并且它有一个 [Ok] 响应,则返回该响应;如果请求有一个 [Err] 响应,则定时器重置,并再次发送新的请求。

最多发送 4 个请求。

use fure::policies::{interval, attempts};
use std::time::Duration;

let get_body = || async {
    reqwest::get("https://www.rust-lang.net.cn")
        .await?
        .text()
        .await
};
let policy = attempts(interval(Duration::from_secs(1)), 3);
let body = fure::retry(get_body, policy).await?;
println!("body = {}", body);

带有退避的顺序重试。

使用指数退避和抖动重试失败的请求。

use fure::{policies::{backoff, cond}, backoff::{exponential, jitter}};
use std::time::Duration;

let get_body = || async {
    reqwest::get("https://www.rust-lang.net.cn")
        .await?
        .text()
        .await
};
let exp_backoff = exponential(Duration::from_secs(1), 2, Some(Duration::from_secs(10)))
    .map(jitter);
let policy = cond(backoff(exp_backoff), |result| !matches!(result, Some(Ok(_))));
let body = fure::retry(get_body, policy).await?;
println!("body = {}", body);

实现您自己的策略。

它的行为类似于上面的间隔策略,但如果它达到 TOO_MANY_REQUESTS,则会在发送下一个请求之前等待几秒钟。

use std::{future::{Future, ready}, pin::Pin, time::Duration};
use fure::Policy;
use reqwest::{Error, Response, StatusCode};

struct RetryPolicy;

impl Policy<Response, Error> for RetryPolicy {
    type ForceRetryFuture = tokio::time::Sleep;

    type RetryFuture = Pin<Box<dyn Future<Output = Self>>>;

    fn force_retry_after(&self) -> Self::ForceRetryFuture {
        tokio::time::sleep(Duration::from_millis(100))
    }

    fn retry(
        self,
        result: Option<Result<&Response, &Error>>,
    ) -> Option<Self::RetryFuture> {
        match result {
            Some(Ok(response)) => match response.status() {
                StatusCode::OK => None,
                StatusCode::TOO_MANY_REQUESTS => {
                    let retry_after_secs: u64 = response
                        .headers()
                        .get("Retry-After")
                        .and_then(|x| x.to_str().ok()?.parse().ok())
                        .unwrap_or(1);
                    Some(Box::pin(async move {
                        tokio::time::sleep(Duration::from_secs(retry_after_secs)).await;
                        self
                    }))
                }
                _ => Some(Box::pin(ready(self))),
            },
            _ => Some(Box::pin(ready(self))),
        }
    }
}

let get_response = || reqwest::get("https://www.rust-lang.net.cn");
let response = fure::retry(get_response, RetryPolicy).await?;
println!("body = {}", response.text().await?);

许可:MIT

依赖项

~2–12MB
~138K SLoC