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-search.example.toml 使用低于 Bitmagnet 默认并发的高吞吐配置 主动 DHT 查询仍共享 max_outbound_queries_per_second 总预算 因此 find_node get_peers 和 sample_infohashes 的总发送速率不会各自叠加后失控
| 配置项 | 运行模板值 | 作用 |
|---|---|---|
max_outbound_queries_per_second |
50 |
三类主动 DHT UDP 查询的合计每秒速率 |
outbound_query_burst |
10 |
空闲后允许立即消费的 UDP 查询数 |
find_node_queries_per_second |
30 |
find_node 自身速率上限 |
find_node_max_in_flight |
64 |
同时等待响应的 find_node 数量 |
new_destinations_per_minute |
1200 |
每分钟首次探测的新 UDP 目标数量 |
peer_lookups_per_second |
8 |
每秒启动的 infohash Peer 查找数量 |
peer_lookup_max_active |
32 |
同时运行的 Peer 查找数量 |
sample_queries_per_second |
10 |
BEP-51 采样查询速率 |
metadata_workers |
64 |
同时处理的 Metadata 任务数量 |
metadata_connects_per_second |
16 |
每秒真正开始的 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 数的综合活跃度分数 不代表全球下载量
这组参数仍保留全局速率限制 如果同机代理或路由器再次出现不稳定应优先降低总 UDP 预算和 Metadata 建连速率
Windows 下索引每五秒批量提交 临时文件占用会自动指数退避重试且不会停止采集 HTTP 服务或丢失 RocksDB 待索引状态
采样去重和 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 /search?q=流浪地球&min_files=1&availability=active&heat=hot&sort=heat
GET /search?q=%5ES%5Cd%7B2%7DE%5Cd%7B2%7D®ex=true
GET /contents/{content_key}?offset=0&limit=20
GET /torrents/{infohash}?file_offset=0&file_limit=100
limit 被限制在 1 到 100 之间且 offset 最大为 10000
搜索支持中文英文数字和文件名片段匹配
设置 regex=true 后查询文本作为不区分大小写的正则表达式匹配名称 别名和文件路径 正则最长 256 字节并由 Tantivy 有限状态自动机执行
过滤参数还包括 min_files max_files first_seen_after first_seen_before last_seen_after last_seen_before availability 和 heat
排序支持 relevance latest oldest heat size_desc size_asc 和 discoveries
有关键词时默认按相关性排序 空查询默认按最近发现排序
搜索结果按 content_key 精确折叠并通过 variant_count 返回变体数量 /contents/{content_key} 用于分页查看全部 infohash 和磁力链接
搜索响应包含 heat 和 availability 摘要 详情响应包含完整验证时间 Peer 数和连续失败次数
详情文件列表默认返回 100 条且单次最多 200 条 使用 file_offset 翻页避免超大种子一次向浏览器返回全部文件
数据恢复
RocksDB 是权威数据源而 Tantivy 是可重建索引
当 Tantivy 目录不存在或结构不匹配时应用会直接创建新索引并从 RocksDB 的内容组状态完成全量重建
项目当前处于开发阶段 持久化结构变化时直接清理测试数据重新采集 不维护旧测试数据库兼容层
正常退出会先停止 DHT 再排空持久化队列提交剩余索引最后关闭 HTTP 服务