mirror of
https://github.com/fish2018/pansou.git
synced 2025-11-25 03:14:59 +08:00
9.7 KiB
9.7 KiB
PanSou 插件系统设计详解
1. 插件系统概述
插件系统是PanSou的核心特性之一,通过统一的接口和自动注册机制,实现了搜索源的可扩展性。该系统允许轻松添加新的网盘搜索插件,而无需修改主程序代码,使系统能够灵活适应不同的搜索需求。
2. 目录结构
pansou/plugin/
├── plugin.go # 插件接口和管理器定义
├── baseasyncplugin.go # 异步插件基类实现
├── jikepan/ # 即刻盘异步插件
├── pan666/ # 盘666异步插件
├── hunhepan/ # 混合盘异步插件
├── pansearch/ # 盘搜插件
├── qupansou/ # 趣盘搜插件
└── panta/ # PanTa插件
3. 插件接口设计
3.1 插件接口定义
插件接口是所有搜索插件必须实现的接口,定义了插件的基本行为。
// SearchPlugin 搜索插件接口
type SearchPlugin interface {
// Name 返回插件名称
Name() string
// Search 执行搜索并返回结果
Search(keyword string) ([]model.SearchResult, error)
// Priority 返回插件优先级(可选,用于控制结果排序)
Priority() int
}
3.2 接口设计思想
- 简单明确:接口只定义了必要的方法,使插件开发简单明了
- 统一返回格式:所有插件返回相同格式的搜索结果,便于统一处理
- 优先级控制:通过Priority方法支持插件优先级,影响结果排序
- 错误处理:Search方法返回error,便于处理搜索过程中的错误
4. 插件注册机制
4.1 全局注册表
插件系统使用全局注册表管理所有插件,通过init函数实现自动注册。
// 全局插件注册表
var (
globalRegistry = make(map[string]SearchPlugin)
globalRegistryLock sync.RWMutex
)
// RegisterGlobalPlugin 注册插件到全局注册表
// 这个函数应该在每个插件的init函数中被调用
func RegisterGlobalPlugin(plugin SearchPlugin) {
if plugin == nil {
return
}
globalRegistryLock.Lock()
defer globalRegistryLock.Unlock()
name := plugin.Name()
if name == "" {
return
}
globalRegistry[name] = plugin
}
// GetRegisteredPlugins 获取所有已注册的插件
func GetRegisteredPlugins() []SearchPlugin {
globalRegistryLock.RLock()
defer globalRegistryLock.RUnlock()
plugins := make([]SearchPlugin, 0, len(globalRegistry))
for _, plugin := range globalRegistry {
plugins = append(plugins, plugin)
}
return plugins
}
4.2 自动注册机制
每个插件通过init函数在程序启动时自动注册到全局注册表。
// 插件实现示例(以jikepan为例)
package jikepan
import (
"pansou/model"
"pansou/plugin"
"pansou/util/json" // 使用优化的JSON库
)
// 确保JikePanPlugin实现了SearchPlugin接口
var _ plugin.SearchPlugin = (*JikePanPlugin)(nil)
// JikePanPlugin 即刻盘搜索插件
type JikePanPlugin struct{}
// init函数在包被导入时自动执行,用于注册插件
func init() {
// 注册插件到全局注册表
plugin.RegisterGlobalPlugin(&JikePanPlugin{})
}
// Name 返回插件名称
func (p *JikePanPlugin) Name() string {
return "jikepan"
}
// Search 执行搜索
func (p *JikePanPlugin) Search(keyword string) ([]model.SearchResult, error) {
// 实现搜索逻辑
// ...
return results, nil
}
// Priority 返回插件优先级
func (p *JikePanPlugin) Priority() int {
return 5 // 优先级为5
}
5. 异步插件系统
5.1 异步插件基类
为了解决某些插件响应时间长的问题,系统提供了BaseAsyncPlugin基类,实现了"尽快响应,持续处理"的异步模式。
// BaseAsyncPlugin 基础异步插件结构
type BaseAsyncPlugin struct {
name string
priority int
client *http.Client // 用于短超时的客户端
backgroundClient *http.Client // 用于长超时的客户端
cacheTTL time.Duration // 缓存有效期
}
// NewBaseAsyncPlugin 创建基础异步插件
func NewBaseAsyncPlugin(name string, priority int) *BaseAsyncPlugin {
// 确保异步插件已初始化
if !initialized {
initAsyncPlugin()
}
// 初始化配置和客户端
// ...
return &BaseAsyncPlugin{
name: name,
priority: priority,
client: &http.Client{
Timeout: responseTimeout,
},
backgroundClient: &http.Client{
Timeout: processingTimeout,
},
cacheTTL: cacheTTL,
}
}
5.2 异步搜索机制
异步插件的核心是AsyncSearch方法,它实现了以下功能:
- 缓存检查:首先检查是否有缓存结果可用
- 双通道处理:同时启动快速响应通道和后台处理通道
- 超时控制:在响应超时时返回当前结果,后台继续处理
- 缓存更新:后台处理完成后更新缓存,供后续查询使用
// AsyncSearch 异步搜索基础方法
func (p *BaseAsyncPlugin) AsyncSearch(
keyword string,
cacheKey string,
searchFunc func(*http.Client, string) ([]model.SearchResult, error),
) ([]model.SearchResult, error) {
// 生成插件特定的缓存键
pluginSpecificCacheKey := fmt.Sprintf("%s:%s", p.name, cacheKey)
// 检查缓存
if cachedItems, ok := apiResponseCache.Load(pluginSpecificCacheKey); ok {
// 处理缓存命中逻辑...
}
// 启动后台处理
go func() {
// 执行搜索,更新缓存...
}()
// 等待响应超时或结果
select {
case results := <-resultChan:
// 返回结果
case err := <-errorChan:
// 返回错误
case <-time.After(responseTimeout):
// 响应超时,返回部分结果
}
}
5.3 异步插件缓存机制
异步插件系统实现了高级缓存机制:
- 持久化存储:缓存定期保存到磁盘,服务重启时自动加载
- 智能缓存管理:基于访问频率、时间和热度的缓存淘汰策略
- 增量更新:新旧结果智能合并,保留唯一标识符不同的结果
- 后台刷新:接近过期的缓存会在后台自动刷新
// 缓存响应结构
type cachedResponse struct {
Results []model.SearchResult
Timestamp time.Time
Complete bool
LastAccess time.Time
AccessCount int
}
// 缓存保存示例
func saveCacheToDisk() {
// 将内存缓存保存到磁盘
// ...
}
// 缓存加载示例
func loadCacheFromDisk() {
// 从磁盘加载缓存到内存
// ...
}
5.4 异步插件实现示例
// HunhepanAsyncPlugin 混合盘搜索异步插件
type HunhepanAsyncPlugin struct {
*plugin.BaseAsyncPlugin
}
// NewHunhepanAsyncPlugin 创建新的混合盘搜索异步插件
func NewHunhepanAsyncPlugin() *HunhepanAsyncPlugin {
return &HunhepanAsyncPlugin{
BaseAsyncPlugin: plugin.NewBaseAsyncPlugin("hunhepan_async", 3),
}
}
// Search 执行搜索并返回结果
func (p *HunhepanAsyncPlugin) Search(keyword string) ([]model.SearchResult, error) {
// 生成缓存键
cacheKey := keyword
// 使用异步搜索基础方法
return p.AsyncSearch(keyword, cacheKey, p.doSearch)
}
// doSearch 实际的搜索实现
func (p *HunhepanAsyncPlugin) doSearch(client *http.Client, keyword string) ([]model.SearchResult, error) {
// 实现具体搜索逻辑
// ...
}
6. 优雅关闭机制
系统实现了优雅关闭机制,确保在程序退出前保存异步插件缓存:
// 在main.go中
// 创建通道来接收操作系统信号
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
// 等待中断信号
<-quit
fmt.Println("正在关闭服务器...")
// 保存异步插件缓存
plugin.SaveCacheToDisk()
// 优雅关闭服务器
if err := srv.Shutdown(ctx); err != nil {
log.Fatalf("服务器关闭异常: %v", err)
}
7. JSON处理优化
为了提高插件的性能,特别是在处理大量JSON数据时,所有插件都使用了高性能的JSON库进行序列化和反序列化操作。
7.1 JSON库选择
PanSou使用字节跳动开发的sonic库替代标准库的encoding/json,提供更高效的JSON处理:
// 使用优化的JSON库
import (
"pansou/util/json" // 内部封装了github.com/bytedance/sonic
)
// 序列化示例
jsonData, err := json.Marshal(reqBody)
// 反序列化示例
if err := json.Unmarshal(respBody, &apiResp); err != nil {
return nil, fmt.Errorf("decode response failed: %w", err)
}
7.2 性能优势
- 更快的序列化/反序列化速度:sonic库比标准库快2-5倍
- 更低的内存分配:减少GC压力
- SIMD加速:利用现代CPU的向量指令集
- 并行处理:大型JSON可以并行处理
7.3 实现方式
所有插件通过统一的内部包装库使用sonic:
// util/json/json.go
package json
import (
"github.com/bytedance/sonic"
)
// API是sonic的全局配置实例
var API = sonic.ConfigDefault
// 初始化sonic配置
func init() {
// 根据需要配置sonic选项
API = sonic.Config{
UseNumber: true,
EscapeHTML: true,
SortMapKeys: false, // 生产环境设为false提高性能
}.Froze()
}
// Marshal 使用sonic序列化对象到JSON
func Marshal(v interface{}) ([]byte, error) {
return API.Marshal(v)
}
// Unmarshal 使用sonic反序列化JSON到对象
func Unmarshal(data []byte, v interface{}) error {
return API.Unmarshal(data, v)
}
这种统一的JSON处理方式确保了所有插件都能获得一致的高性能,特别是在处理大量搜索结果时,显著提升了系统整体响应速度。