tencentcloud_sms/
client.rs

1use crate::{
2    config::ClientConfig,
3    credential::Credential,
4    error::{SmsError, SmsResult},
5};
6use reqwest::Client as HttpClient;
7use std::time::Duration;
8
9pub(crate) const API_VERSION: &str = "2021-01-11";
10
11#[derive(Clone)]
12pub struct SmsClient {
13    pub(crate) credential: Credential,
14    pub(crate) config: ClientConfig,
15    pub(crate) http_client: HttpClient,
16}
17
18impl SmsClient {
19    /// 创建新的SMS客户端
20    ///
21    /// # 参数
22    /// - `credential`: API凭证 (SecretId/SecretKey)
23    /// - `config`: 客户端配置
24    ///
25    /// # 错误
26    /// - 配置验证失败
27    /// - HTTP客户端构建失败(如代理配置错误)
28    pub fn new(credential: Credential, config: ClientConfig) -> SmsResult<Self> {
29        // 验证配置
30        config.validate()?;
31
32        let mut builder = HttpClient::builder()
33            .timeout(config.timeout)
34            .pool_idle_timeout(Duration::from_secs(90))
35            .pool_max_idle_per_host(10);
36
37        if let Some(proxy_url) = &config.proxy {
38            let proxy = reqwest::Proxy::all(proxy_url)
39                .map_err(|e| SmsError::Configuration(format!("代理配置错误: {}", e)))?;
40            builder = builder.proxy(proxy);
41        }
42
43        let http_client = builder
44            .build()
45            .map_err(|e| SmsError::Configuration(format!("HTTP客户端构建失败: {}", e)))?;
46
47        Ok(Self {
48            credential,
49            config,
50            http_client,
51        })
52    }
53
54    /// 计算退避延迟
55    ///
56    /// 实现指数退避 + 随机抖动算法
57    pub(crate) fn calculate_backoff(&self, attempt: u64, suggested: Option<Duration>) -> Duration {
58        // 如果错误提供了重试延迟建议,优先使用
59        if let Some(delay) = suggested {
60            return delay;
61        }
62
63        let retry_config = match &self.config.retry {
64            Some(c) => c,
65            None => return Duration::from_secs(1),
66        };
67
68        // 指数退避: initial * multiplier^(attempt-1)
69        let mut delay = retry_config.initial_backoff.as_millis() as f64
70            * retry_config.backoff_multiplier.powi(attempt as i32 - 1);
71
72        // 添加随机抖动 (±25%)
73        if retry_config.add_jitter {
74            use rand::Rng;
75            let jitter = rand::rng().random_range(0.75..=1.25);
76            delay *= jitter;
77        }
78
79        // 限制最大延迟
80        Duration::from_millis(delay.min(retry_config.max_backoff.as_millis() as f64) as u64)
81    }
82}