[p2p-Magnet] 数据模型(GORM) | DHT爬虫 | 分类器
第3章:数据模型(GORM)
在第2章:搜索查询引擎中,我们探索了系统如何通过高级数据库功能处理搜索请求。现在让我们深入了解这些"书籍"(种子资源)在系统"图书馆"中是如何组织和存储的。
数据模型与GORM解析
数据模型:信息蓝图
如同建筑需要设计图纸,数据模型定义了:
- 存储内容:种子哈希值、名称、大小等核心字段
- 结构关系:种子与文件的一对多关系
- 外部关联:种子与影视元数据的映射关系
GORM:数据库管家
作为Go语言的ORM框架,GORM实现:
- 对象映射:将Go结构体与数据库表自动关联
- 关系管理:处理一对多/多对多等复杂关系
- 语法简化:用
db.First()
替代复杂SQL语句
核心数据模型
系统主要包含五类核心模型:
- 种子模型(Torrent)
type Torrent struct {InfoHash protocol.ID // 唯一标识Name string // 原始名称Size uint // 字节大小Files []TorrentFile // 关联文件
}
- 文件模型(TorrentFile)
type TorrentFile {InfoHash protocol.ID // 所属种子Path string // 文件路径Size uint // 文件大小
}
- 标签模型(TorrentTag)
type TorrentTag {InfoHash protocol.ID // 所属种子Name string // 标签名称(如"4K")
}
- 内容分类模型(TorrentContent)
type TorrentContent {InfoHash protocol.ID // 所属种子ContentType string // 内容类型(movie/tv)ContentID string // 外部ID(如TMDB ID)
}
- 元数据模型(Content)
type Content {ID string // 外部IDTitle string // 标准名称ReleaseYear int // 发布年份
}
数据关联示意图
实战示例:存储新种子
当系统发现《黑客帝国》种子时:
- 创建种子记录
newTorrent := model.Torrent{InfoHash: "a1b2c3...", Name: "The.Matrix.1999.1080p",Size: 10_000_000_000
}
- 添加文件记录
files := []model.TorrentFile{{Path: "The.Matrix.mkv", Size: 9_800_000_000},{Path: "sample.mp4", Size: 200_000_000}
}
- 关联元数据
content := model.TorrentContent{ContentType: "movie",ContentID: "tmdb_603" // TMDB ID
}
- 自动生成SQL
/* GORM自动生成 */
INSERT INTO torrents VALUES ('a1b2c3...', 'The.Matrix...', 10000000000);
INSERT INTO torrent_files VALUES ('a1b2c3...', 0, 'The.Matrix.mkv', 9800000000),('a1b2c3...', 1, 'sample.mp4', 200000000);
技术实现细节
模型自动生成
通过gorm.io/gen
工具自动创建:
// internal/database/gen/gen.go
g.GenerateModel("torrents",gen.FieldRelate(field.HasMany, "Files", g.GenerateModel("torrent_files"),&field.RelateConfig{GORMTag: field.GormTag{"foreignKey":"InfoHash"}})
)
数据库连接
PostgreSQL连接配置:
// internal/database/gorm.go
gorm.Open(postgres.New(postgres.Config{Conn: sqlDB, // 连接池
}))
总结
数据模型与GORM共同构建了系统的存储架构:
- 结构化存储:明确定义各类数据字段
- 高效关联:智能管理复杂数据关系
- 开发提效:简化数据库操作复杂度
下一章将揭示系统如何发现种子资源:DHT爬虫
第4章:DHT爬虫
在第3章:数据模型(GORM)中,我们了解了系统如何通过"设计蓝图"和"施工队长"来组织存储种子信息。但这些种子最初是如何被发现的?系统如何不依赖传统网站或追踪器就能获取它们?
这就是DHT爬虫的职责所在!它如同系统的探险家,持续在去中心化的BitTorrent网络中搜寻新种子,构建独立的种子索引库。
DHT爬虫解析
工作原理
想象互联网是藏满宝藏的岛屿,传统方式依赖可能过时的"藏宝图"(中心化追踪器)。DHT爬虫则像装备声纳的探险船,直接探索**分布式哈希表(DHT)**网络——这个由全体BitTorrent客户端共同维护的"电话簿"。
核心功能:
- 发现哈希值:通过询问其他客户端"你知道哪些种子?"获取信息哈希(种子的唯一指纹)
- 获取元数据:请求种子的"配方卡"(包含名称、文件列表等)
- 处理存储:将元数据传递给分类系统并存入数据库
操作方式
用户主要通过以下方式交互:
- 配置调整:可设置爬取强度(
scaling_factor
)或初始连接节点 - 状态监控:通过WebUI仪表盘查看实时指标,如
bitmagnet_dht_crawler_persisted_total
技术实现细节
核心处理流程
关键代码模块
- 控制中心(
crawler.go
)
type crawler struct {kTable ktable.Table // DHT网络节点表client client.Client // DHT通信客户端metainfoRequester metainforequester.Requester // 元数据请求器nodesForSampleInfoHashes chan ktable.Node // 采样节点通道
}func (c *crawler) start() {go c.runSampleInfoHashes() // 启动哈希采样协程go c.runPersistTorrents() // 启动存储协程// ...其他工作协程...
}
- 哈希采样(
sample_infohashes.go
)
func (c *crawler) runSampleInfoHashes() {for node := range c.nodesForSampleInfoHashes {res := c.client.SampleInfoHashes(node) // 请求节点采样for _, hash := range res.Samples {if !c.ignoreHashes.testAndAdd(hash) { // 布隆过滤器去重c.infoHashTriage <- hash // 传递新哈希}}}
}
- 数据存储(
persist.go
)
func (c *crawler) runPersistTorrents() {for batch := range c.persistTorrents {c.dao.Transaction(func(tx *dao.Query) error {tx.Torrent.CreateInBatches(batch.torrents, 100) // 批量存储种子tx.TorrentFile.CreateInBatches(batch.files, 100) // 批量存储文件return nil})}
}
总结
DHT爬虫通过:
- 主动探索DHT网络
- 智能过滤重复项
- 高效批量存储
实现系统的自给自足式种子发现。下一章将揭示如何解析种子内容:分类器
第5章:分类器
在第4章:DHT爬虫中,我们看到系统如何持续探索BitTorrent网络并获取种子基础信息。但面对如"The.Matrix.1999.1080p.x264.DTS-HD.MA.5.1-GROUP"这样的原始数据,系统需要理解其实际含义——这就是分类器的使命。
分类器解析
核心功能
分类器如同智能图书管理员,能够:
- 内容归类:识别电影/电视剧/音乐等类型
- 特征提取:解析分辨率/音频编码等信息
- 元数据增强:通过TMDB等API获取官方信息
- 自定义标记:添加用户定义标签
- 内容过滤:自动屏蔽违规资源
规则配置
通过YAML文件定义分类规则,支持:
- 工作流(Workflow)
workflows:default:- if_else:condition: "torrent.baseName.matches(keywords.banned)"if_action: delete- if_else:condition: "result.contentType == contentType.unknown"if_action: find_match
-
执行动作(Action)
包含set_content_type
/add_tag
/delete
等操作指令 -
条件判断(Condition)
采用CEL表达式语言,可调用:
torrent
对象:种子基础信息result
对象:当前分类结果- 预定义关键词列表
- 关键词列表(Keywords)
keywords:music: [music, album]banned: [违禁词1, 违禁词2]
实战案例
案例1:自动过滤内容
# config.yml
classifier:flags:delete_xxx: truekeywords:banned: [暴力内容, 政治敏感]
案例2:自定义标签
# classifier.yml
workflows:my_workflow:- run_workflow: default- if_else:condition: "torrent.baseName.matches(keywords.classic)"if_action: add_tag: 经典老片
技术实现
核心处理流程
关键代码
- 处理器入口(
processor.go
)
func (p processor) Process(ctx context.Context, torrent model.Torrent) error {result, err := p.classifier.Run(ctx, torrent) // 调用分类器if err != nil {return err}return p.dao.TorrentContent.Clauses(clause.OnConflict{UpdateAll: true,}).Create(&model.TorrentContent{InfoHash: torrent.InfoHash,ContentType: result.ContentType,// ...其他字段...})
}
- 规则编译(
compiler.go
)
func (c compiler) Compile(source Source) (Runner, error) {workflows := make(map[string][]Action)for name, workflow := range source.Workflows {for _, action := range workflow.Actions {compiledAction, err := c.compileAction(action)workflows[name] = append(workflows[name], compiledAction)}}return runner{workflows: workflows}, nil
}
总结
分类器通过:
- 可定制规则引擎
- 多数据源关联
- 智能条件判断
实现种子内容的深度解析。下一章将揭示任务调度机制:队列与处理器