13个版本
使用旧的Rust 2015
0.4.2 | 2018年2月28日 |
---|---|
0.3.4 | 2017年11月29日 |
0.3.0 | 2017年5月12日 |
0.2.2 | 2017年3月31日 |
0.1.1 |
|
#399 in 并发
在 cernan 中使用
61KB
1K SLoC
hopper - 无界mpsc且内存限制
Hopper提供了一个版本的Rust标准mpsc,它是无界的但消耗的内存量有限。这是通过在需要时将元素分页到磁盘来实现的。这里的雄心是支持mpsc风格的通信,而不分配无界的内存或丢弃输入。
快速入门
在Cargo.toml中包含hopper库
hopper= "0.4"
并像使用stdlib的mpsc一样使用它
extern crate tempdir;
extern crate hopper;
let dir = tempdir::TempDir::new("hopper").unwrap();
let (mut snd, mut rcv) = hopper::channel("example", dir.path()).unwrap();
snd.send(9);
assert_eq!(Some(9), rcv.iter().next());
这里的主要区别是您必须提供一个通道名称和hopper可以分页到磁盘的目录。
Hopper旨在用于那些您的系统不能卸载输入并且必须最终处理它们的场景。当内存缓冲区满时,Hopper会在磁盘溢出时维护一个输入的内存缓冲区。虽然hopper会分页到磁盘,但它不会像stdlib mpsc那样在重启时保留写入。
幕后细节
Hopper的通道看起来非常像Unix中的命名管道。您为channel_2
或channel_with_max_bytes_3
提供名称,并在其中推入和拉出字节。磁盘分页增加了一个复杂性。在私下里,提供给上述两个函数的名称用于在data_dir
下创建一个目录。在这种情况下,该目录会填充单调递增的文件。从现在开始,我们将专门处理这个问题。
磁盘结构如下
data-dir/
sink-name0/
0
1
sink-name1/
0
您会注意到在这个模块的命名空间中导出了Sender和Receiver。这些是支持命名通道发送和接收端的结构。Sender(可能有多个)负责创建“队列文件”。在上面的示例中,data-dir/sink-name*/*
是队列文件。这些文件由Sender作为追加日志处理。Receiver会遍历这些日志以读取其中序列化的数据。
这不会填满我的磁盘吗?
也许吧!每个发送者都有一个关于它可能读取的最大字节数的概念,这个值在创建通道时可以显式设置,使用channel_with_max_bytes
,一旦发送者超过这个限制,它会尝试将队列文件标记为只读并创建一个新文件。接收者被编程为读取当前的队列文件,直到遇到文件结束标记EOF并发现文件为只读,此时它会删除文件——它是唯一的读取者——然后继续处理下一个。
hopper::channel_with_max_bytes
接受一个max_disk_files
参数,定义了可以同时存在的溢出文件的总数。如果所有内存和磁盘缓冲区都已满,则队列中的发送将失败。错误结果被写入,以便调用者可以恢复对输入值的所有权。默认情况下,max_disk_files == usize::max_value()
,因此如果接收者无法跟上发送者的速度,那么,糟糕,你的磁盘会逐渐被填满。
我需要哪些类型的文件系统选项?
Hopper旨在在任何奇特的旧文件系统上以任何选项运行,即使在高并发下也能如此。由于常见的文件系统不支持交错小原子写入,hopper一次限制为一个独占发送者和一个独占接收者。这可能会限制mpsc的并发性,但可以保持数据完整性。我们对此领域的改进持开放态度。
hopper的性能如何?
Hopper附带基准测试。我们观察到,性能仅比stdlib的mpsc慢30%,最高可达70倍,具体取决于配置选项、磁盘I/O速度等因素。我们强烈建议您在自己的系统上进行基准测试。
依赖项
~0.7–1MB
~21K SLoC