fast_down_ffi/config.rs
1use fast_down::{ProgressEntry, Proxy};
2use parking_lot::Mutex;
3use std::{collections::HashMap, net::IpAddr, sync::Arc, time::Duration};
4
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6#[derive(Debug, Clone, PartialEq, Eq, Default)]
7pub enum WriteMethod {
8 #[default]
9 Mmap,
10 Std,
11}
12
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14#[derive(Debug, Clone)]
15pub struct Config {
16 /// 线程数量,推荐值 `32` / `16` / `8`。线程越多不意味着越快
17 pub threads: usize,
18 /// 设置代理,支持 https、http、socks5 代理
19 pub proxy: Proxy<String>,
20 /// 自定义请求头
21 pub headers: HashMap<String, String>,
22 /// 最小分块大小,单位为字节,推荐值 `8 * 1024 * 1024`
23 ///
24 /// - 分块太小容易造成强烈竞争
25 /// - 当无法分块的时候会进入冗余竞争模式
26 pub min_chunk_size: u64,
27 /// 写入缓冲区大小,单位为字节,推荐值 `16 * 1024 * 1024`
28 ///
29 /// - 只对 [`WriteMethod::Std`] 写入方法有效,有利于将随机写入转换为顺序写入,提高写入速度
30 /// - 对于 [`WriteMethod::Mmap`] 写入方法无效,因为写入缓冲区由系统决定
31 pub write_buffer_size: usize,
32 /// 字节合并区最大大小,单位为字节,推荐值 `16 * 1024 * 1024` 或比 `write_buffer_size` 略大
33 ///
34 /// 当字节合并区大小 >= `cache_high_watermark` 会触发一次合并写入,把字节合并区的大小降到 `cache_low_watermark` 及以下
35 ///
36 /// - 只对 [`WriteMethod::Std`] 写入方法有效,有利于将随机写入转换为顺序写入,提高写入速度
37 /// - 对于 [`WriteMethod::Mmap`] 写入方法无效
38 pub cache_high_watermark: usize,
39 /// 字节合并区目标大小,单位为字节,推荐值 `8 * 1024 * 1024` 或为 `cache_high_watermark` 的一半
40 ///
41 /// 字节合并区在触发合并写入后,大小会降到 `cache_low_watermark` 及以下
42 ///
43 /// - 只对 [`WriteMethod::Std`] 写入方法有效,有利于将随机写入转换为顺序写入,提高写入速度
44 /// - 对于 [`WriteMethod::Mmap`] 写入方法无效
45 pub cache_low_watermark: usize,
46 /// 写入队列容量,推荐值 `10240`
47 ///
48 /// 如果下载线程太快,填满了写入队列,会触发压背,降低下载速度,防止内存占用过大
49 pub write_queue_cap: usize,
50 /// 请求失败后的默认重试间隔,推荐值 `500ms`
51 ///
52 /// 如果服务器返回中有 `Retry-After` 头,则遵循服务器返回的设定
53 pub retry_gap: Duration,
54 /// 拉取超时时间,推荐值 `5000ms`
55 ///
56 /// 请求发出后,接收字节中,如果在 `pull_timeout` 这一段时间内一个字节也没收到,则中断连接,重新请求。
57 /// 有利于触发 TCP 重新检测拥塞状态,提高下载速度
58 pub pull_timeout: Duration,
59 /// 是否接受无效证书(危险),推荐值 `false`
60 pub accept_invalid_certs: bool,
61 /// 是否接受无效主机名(危险),推荐值 `false`
62 pub accept_invalid_hostnames: bool,
63 /// 写入磁盘方式,推荐值 [`WriteMethod::Mmap`]
64 ///
65 /// - [`WriteMethod::Mmap`] 写入方式速度最快,将写入交给操作系统执行,但是:
66 /// 1. 在 32 位系统上最大只能映射 4GB 的文件,所以在 32 位系统上,会自动回退到 [`WriteMethod::Std`]
67 /// 2. 必须知道文件大小,否则会自动回退到 [`WriteMethod::Std`]
68 /// 3. 特殊情况下会出现系统把所有数据全部缓存在内存中,下载完成后一次性写入磁盘,造成下载完成后长时间卡顿
69 /// - [`WriteMethod::Std`] 写入方式兼容性最好,会在 `write_buffer_size` 内对片段进行排序,尽量转换为顺序写入
70 #[cfg(feature = "file")]
71 pub write_method: WriteMethod,
72 /// 设置获取元数据的重试次数,推荐值 `10`。注意,这不是下载中的重试次数
73 pub retry_times: usize,
74 /// 使用哪些地址来发送请求,推荐值 `Vec::new()`
75 ///
76 /// 如果你有多个网卡可用,可以填写他们的对外 IP 地址,请求会在这些 IP 地址上轮换,下载不一定会更快
77 pub local_address: Vec<IpAddr>,
78 /// 冗余线程数,推荐值 `3`
79 ///
80 /// 当块大小小于 `min_chunk_size` 后无法分块,进入冗余竞争模式。
81 /// 最多有 `max_speculative` 个线程在同一分块上竞争下载,以解决下载卡进度 99% 的问题
82 pub max_speculative: usize,
83 /// 已经下载过的部分,如果你想下载整个文件,就传 `Vec::new()`
84 pub downloaded_chunk: Arc<Mutex<Vec<ProgressEntry>>>,
85 /// 已下载分块的平滑窗口,单位为字节,推荐值 `8 * 1024`
86 ///
87 /// 它会过滤掉 `downloaded_chunk` 中小于 `chunk_window` 的小空洞,以减小 HTTP 请求数量
88 pub chunk_window: u64,
89}
90
91impl Default for Config {
92 fn default() -> Self {
93 Self {
94 retry_times: 10,
95 threads: 32,
96 proxy: Proxy::System,
97 headers: HashMap::new(),
98 min_chunk_size: 8 * 1024 * 1024,
99 write_buffer_size: 16 * 1024 * 1024,
100 cache_high_watermark: 16 * 1024 * 1024,
101 cache_low_watermark: 8 * 1024 * 1024,
102 write_queue_cap: 10240,
103 retry_gap: Duration::from_millis(500),
104 pull_timeout: Duration::from_secs(5),
105 accept_invalid_certs: false,
106 accept_invalid_hostnames: false,
107 local_address: Vec::new(),
108 max_speculative: 3,
109 #[cfg(feature = "file")]
110 write_method: WriteMethod::Mmap,
111 downloaded_chunk: Arc::default(),
112 chunk_window: 8 * 1024,
113 }
114 }
115}