Files
pansou/docs/4-插件系统设计.md
www.xueximeng.com 5a3917c999 异步插件缓存
2025-07-15 00:03:02 +08:00

9.7 KiB
Raw Blame History

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 接口设计思想

  1. 简单明确:接口只定义了必要的方法,使插件开发简单明了
  2. 统一返回格式:所有插件返回相同格式的搜索结果,便于统一处理
  3. 优先级控制通过Priority方法支持插件优先级影响结果排序
  4. 错误处理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方法它实现了以下功能

  1. 缓存检查:首先检查是否有缓存结果可用
  2. 双通道处理:同时启动快速响应通道和后台处理通道
  3. 超时控制:在响应超时时返回当前结果,后台继续处理
  4. 缓存更新:后台处理完成后更新缓存,供后续查询使用
// 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 异步插件缓存机制

异步插件系统实现了高级缓存机制:

  1. 持久化存储:缓存定期保存到磁盘,服务重启时自动加载
  2. 智能缓存管理:基于访问频率、时间和热度的缓存淘汰策略
  3. 增量更新:新旧结果智能合并,保留唯一标识符不同的结果
  4. 后台刷新:接近过期的缓存会在后台自动刷新
// 缓存响应结构
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处理方式确保了所有插件都能获得一致的高性能特别是在处理大量搜索结果时显著提升了系统整体响应速度。