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
网络保护配置
主动 DHT 查询共享 max_outbound_queries_per_second 总预算,因此 find_node get_peers 和 sample_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 active 和 possibly_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_succeeded 和 peer_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 /torrents/{infohash}
limit 被限制在 1 到 100 之间且 offset 最大为 10000
搜索和大小扩展名过滤由 Tantivy 索引执行不会把全部记录加载到内存过滤
搜索响应包含 heat 和 availability 摘要 详情响应包含完整验证时间 Peer 数和连续失败次数
数据恢复
RocksDB 是权威数据源而 Tantivy 是可重建索引
当 Tantivy 目录不存在时应用会把全部 RocksDB 记录重新标记为待索引并自动完成全量重建
RocksDB v1 会在启动时通过可恢复迁移升级到 v2 旧 Tantivy schema 会先备份再从 RocksDB 重建 重建提交完成后清理备份
正常退出会先停止 DHT 再排空持久化队列提交剩余索引最后关闭 HTTP 服务