Files
dht/dht-search

dht-search

dht-search 是集 DHT Metadata 采集 RocksDB 持久化 Tantivy 搜索索引和 HTTP API 于一体的应用

构建准备

RocksDB 包含 C++ 代码并在构建时使用 bindgen 因此需要 libclang

Windows 可以把 libclang 安装到工作区本地目录

python -m pip install --target .tools\libclang libclang
$env:LIBCLANG_PATH = "$PWD\.tools\libclang\clang\native"
cargo build -p dht-search --release

.tools 只用于本地构建不会部署到运行设备

配置

复制根目录的配置模板

Copy-Item dht-search.example.toml dht-search.toml

相对 data_dir 以配置文件所在目录为基准解析

也可以通过命令行覆盖数据目录和本次运行时长

cargo run -p dht-search -- --data-dir D:\data\dht-search --run-duration-secs 3600

不设置 run-duration-secs 时服务持续运行直到收到 Ctrl+C SIGINT 或 SIGTERM

生产 Web 页面需要先在 web 目录执行 bun run build Axum 会从 http.web_dir 提供构建结果

网络保护配置

主动 DHT 查询共享 max_outbound_queries_per_second 总预算,因此 find_node get_peerssample_infohashes 的总发送速率不会各自叠加后失控

配置项 保守默认值 作用
max_outbound_queries_per_second 10 三类主动 DHT UDP 查询的合计每秒速率
outbound_query_burst 2 空闲后允许立即消费的 UDP 查询数
find_node_queries_per_second 6 find_node 自身速率上限
find_node_max_in_flight 12 同时等待响应的 find_node 数量
new_destinations_per_minute 60 每分钟首次探测的新 UDP 目标数量
peer_lookups_per_second 1 每秒启动的 infohash Peer 查找数量
peer_lookup_max_active 4 同时运行的 Peer 查找数量
sample_queries_per_second 1 BEP-51 采样查询速率
metadata_workers 8 同时处理的 Metadata 任务数量
metadata_connects_per_second 2 每秒真正开始的 Peer TCP 连接数量

Metadata 下载和可用性握手共用 metadata_connects_per_second 预算不会各自叠加

按需可用性验证

搜索结果和详情访问只会把已过冷却期的种子异步加入持久化验证队列 HTTP 响应不会等待 DHT 或 Peer 网络

配置项 默认值 作用
verification.enabled true 是否启用按需可用性验证
verification.queue_capacity 10000 持久化验证队列容量
verification.max_active 2 同时验证的种子数量
verification.max_peer_attempts 3 每个种子最多握手的 Peer 数量
verification.lease_secs 60 异常退出后验证任务重新可领取的租约时间
verification.poll_interval_millis 250 持久化队列轮询间隔

详情访问使用高优先级 搜索结果使用普通优先级 队列满时高优先级可以替换最旧普通任务

可用性分为 unknown activepossibly_stale 一次或多次验证失败只表示当前可能没有可连接 Peer 不会删除种子

新抓取记录会把成功下载 Metadata 的来源 Peer 视为一次有效验证 旧记录按需复查时会同时使用新 DHT 结果和已保存的成功来源 Peer

热度是近期 DHT 发现强度最近出现时间和可连接 Peer 数的综合活跃度分数 不代表全球下载量

桌面网络不要在不了解路由器 NAT 和代理容量时大幅提高这些值

采样去重和 Peer 查找

BEP-51 返回的 infohash 会先进入有界批量准入队列并由 RocksDB 精确判断

已有 infohash 只更新最后发现时间和发现次数不会再次执行 Peer Lookup

未知 infohash 首先只向返回样本的 DHT 节点查询一次 get_peers 只有单点查询没有返回 Peer 时才降级为有限迭代查找

/stats 中的 sampled_hashes_filtered peer_lookup_preferred_succeededpeer_lookup_fallbacks 用于观察提前去重和单点优先效果

Linux 资源限制

生产环境仍可能同时使用较多 TCP socket

Linux 生产运行必须把文件描述符上限提高到至少 65536

prlimit --nofile=65536:65536 -- \
  /opt/dht-search/dht-search --config /opt/dht-search/dht-search.toml

systemd 服务需要设置

[Service]
LimitNOFILE=65536

文件描述符上限过低时 DHT 连接会挤占 Tantivy 和 RocksDB 打开文件所需的描述符并导致服务安全停止

API

默认只监听 127.0.0.1:8080

GET /health
GET /ready
GET /stats
GET /search?q=ubuntu&offset=0&limit=20
GET /search?q=&min_size=1048576&max_size=10737418240&extension=mkv
GET /search?q=流浪地球&min_files=1&availability=active&heat=hot&sort=heat
GET /contents/{content_key}?offset=0&limit=20
GET /torrents/{infohash}

limit 被限制在 1 到 100 之间且 offset 最大为 10000

搜索支持中文英文数字和文件名片段匹配

过滤参数还包括 min_files max_files first_seen_after first_seen_before last_seen_after last_seen_before availabilityheat

排序支持 relevance latest oldest heat size_desc size_ascdiscoveries

有关键词时默认按相关性排序 空查询默认按最近发现排序

搜索结果按 content_key 精确折叠并通过 variant_count 返回变体数量 /contents/{content_key} 用于分页查看全部 infohash 和磁力链接

搜索响应包含 heatavailability 摘要 详情响应包含完整验证时间 Peer 数和连续失败次数

数据恢复

RocksDB 是权威数据源而 Tantivy 是可重建索引

当 Tantivy 目录不存在或结构不匹配时应用会直接创建新索引并从 RocksDB 的内容组状态完成全量重建

项目当前处于开发阶段 持久化结构变化时直接清理测试数据重新采集 不维护旧测试数据库兼容层

正常退出会先停止 DHT 再排空持久化队列提交剩余索引最后关闭 HTTP 服务