1use rand::Rng;
2use rand::rng;
3use reqwest::Client;
4use std::time::Duration;
5use tokio::time::sleep;
6
7async fn fetch_once(
9 client: &Client,
10 url: &str,
11) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
12 let resp = client.get(url).send().await?;
13 let text = resp.text().await?;
14 Ok(text)
15}
16
17pub async fn fetch_with_retry(
22 client: &Client,
23 url: &str,
24) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
25 let retry_attempts = 10;
26
27 for i in 0..retry_attempts {
28 match fetch_once(client, url).await {
29 Ok(text) => {
30 return Ok(text);
31 }
32 Err(e) => {
33 eprintln!(
34 "HTTP取得エラー (attempt {} / {}): {}",
35 i + 1,
36 retry_attempts,
37 e
38 );
39 let random_part = {
41 let mut local_rng = rng();
42 local_rng.random_range(0.0..1.0)
43 };
44 let sleep_time = (2u64.pow(i) as f64) + random_part;
45 sleep(Duration::from_secs_f64(sleep_time)).await;
46 }
47 }
48 }
49
50 Err(format!(
51 "{}回試みてもデータを取得できませんでした: {}",
52 retry_attempts, url
53 )
54 .into())
55}