dragonfly写文件工作原理

dragonfly写文件,是按照taskId放置文件,文件路径 /var/lib/dragonfly/content/tasks/[taskID头3个字符串]/taskID文件

1. 下载task前

在resource/task.rs/fn download_started方法中,会先调fn create_task,创建路径和文件。在创建文件的时候,会调用fallocate,按照taskID的content_length预分配文件大小。这种文件,相当于逻辑大小跟content_length相同(ls -lh), 但是实际物理大小为0(du -sh)。文件会被打洞(0值)变成稀疏文件。读文件时,返回的是空。

# 将 file.img 中从 100M 到 500M 的区域“打洞”(变成全0的稀疏空洞)
fallocate -p -o 100M -l 400M file.img

这个文件,可能会在gc时期被taskTTL和memory规则删除。

2. 下载task各个piece时

task piece是并行下载,下载完成的顺序并不能保证。

每个piece下载完成时,会调用resource/piece.rs/fn down_from_parent方法中,调用fn download_piece_from_parent_finished,接着调用fn write_piece,这个就是核心写task文件方法。

它会先seek查找piece的offset,从offset开始写入piece。在写入时,用配置的write_buffer_size创建带缓冲的写入器写入,减少系统调用次数。而且边读(从传参reader读)边写边算crc32。

// 第383: 创建 crc32fast 哈希计算器
let mut hasher = crc32fast::Hasher::new();

// 第384-386: InspectReader 包装原始 reader,每读一块数据就回调闭包更新哈希
let mut tee = InspectReader::new(reader, |bytes| {
    hasher.update(bytes);  // 增量式更新 CRC32
});

// 第388-390: io::copy 将数据从 tee 拷贝到 writer(文件),同时触发 InspectReader 的回调
let length = io::copy(&mut tee, &mut writer).await?;

// 第399: 所有数据拷贝完成后,finalize 得到最终 CRC32 值
hash: hasher.finalize().to_string(),

crc32计算的核心思路:利用 InspectReader 的拦截机制,在 io::copy 的数据流转过程中,每读一个 buffer 就同步更新 CRC32 哈希。一次遍历,同时完成写入和校验,无需额外遍历数据。

CRC32 哈希用于验证从其他 Peer 下载的 piece 数据是否完整正确。具体流程:

  1. 从父节点(Parent Peer)下载 piece 时,调度器会提供该 piece 的期望 digest(expected_digest,格式为 crc32:<hash值>)
  2. 本地写入 piece 时计算实际 CRC32 哈希
  3. 两者对比,如果不一致则返回 DigestMismatch 错误,说明数据在传输中被损坏

 

posted on 2026-09-15 16:28  月影缤纷  阅读(12)  评论(0)    收藏  举报