4.7.4 插件动态加载

4.7.4 插件动态加载 #

动态加载是插件系统的核心功能之一,它允许应用程序在运行时加载、卸载和重新加载插件,而无需重启整个应用程序。本节将详细介绍如何在 Go 中实现插件的动态加载,包括使用 Go 的 plugin 包、共享库加载、热插拔机制等高级特性。

Go Plugin 包 #

基础插件加载 #

Go 1.8 引入了 plugin 包,支持动态加载 Go 插件。插件必须编译为共享库(.so 文件)。

插件代码示例(plugin_example.go):

package main

import (
    "fmt"
    "time"
)

// 插件必须是 main 包

// PluginInfo 插件信息结构
type PluginInfo struct {
    Name        string
    Version     string
    Description string
}

// Calculator 计算器接口
type Calculator interface {
    Add(a, b int) int
    Multiply(a, b int) int
}

// SimpleCalculator 简单计算器实现
type SimpleCalculator struct{}

func (sc *SimpleCalculator) Add(a, b int) int {
    return a + b
}

func (sc *SimpleCalculator) Multiply(a, b int) int {
    return a * b
}

// 导出的变量和函数
var Info = PluginInfo{
    Name:        "SimpleCalculator",
    Version:     "1.0.0",
    Description: "A simple calculator plugin",
}

var Calc Calculator = &SimpleCalculator{}

// Initialize 初始化函数
func Initialize() error {
    fmt.Printf("Plugin %s v%s initialized at %s\n",
               Info.Name, Info.Version, time.Now().Format("2006-01-02 15:04:05"))
    return nil
}

// Cleanup 清理函数
func Cleanup() error {
    fmt.Printf("Plugin %s cleaned up at %s\n",
               Info.Name, time.Now().Format("2006-01-02 15:04:05"))
    return nil
}

// ProcessData 数据处理函数
func ProcessData(data []int) []int {
    result := make([]int, len(data))
    for i, v := range data {
        result[i] = v * 2
    }
    return result
}

// main 函数在插件中是必需的,但不会被调用
func main() {}

编译插件:

go build -buildmode=plugin -o calculator.so plugin_example.go

主程序加载插件:

package main

import (
    "fmt"
    "log"
    "plugin"
    "reflect"
)

// PluginInfo 插件信息结构(与插件中的定义相同)
type PluginInfo struct {
    Name        string
    Version     string
    Description string
}

// Calculator 计算器接口(与插件中的定义相同)
type Calculator interface {
    Add(a, b int) int
    Multiply(a, b int) int
}

func main() {
    // 加载插件
    p, err := plugin.Open("calculator.so")
    if err != nil {
        log.Fatalf("Failed to load plugin: %v", err)
    }

    // 查找插件信息
    infoSymbol, err := p.Lookup("Info")
    if err != nil {
        log.Fatalf("Failed to find Info symbol: %v", err)
    }

    info, ok := infoSymbol.(*PluginInfo)
    if !ok {
        log.Fatalf("Info symbol is not of type *PluginInfo")
    }

    fmt.Printf("Loaded plugin: %s v%s - %s\n",
               info.Name, info.Version, info.Description)

    // 查找初始化函数
    initSymbol, err := p.Lookup("Initialize")
    if err != nil {
        log.Fatalf("Failed to find Initialize symbol: %v", err)
    }

    initFunc, ok := initSymbol.(func() error)
    if !ok {
        log.Fatalf("Initialize symbol is not a function")
    }

    // 初始化插件
    if err := initFunc(); err != nil {
        log.Fatalf("Failed to initialize plugin: %v", err)
    }

    // 查找计算器对象
    calcSymbol, err := p.Lookup("Calc")
    if err != nil {
        log.Fatalf("Failed to find Calc symbol: %v", err)
    }

    calc, ok := calcSymbol.(*Calculator)
    if !ok {
        log.Fatalf("Calc symbol is not of type *Calculator")
    }

    // 使用插件功能
    calculator := *calc
    result1 := calculator.Add(10, 20)
    result2 := calculator.Multiply(5, 6)

    fmt.Printf("10 + 20 = %d\n", result1)
    fmt.Printf("5 * 6 = %d\n", result2)

    // 查找数据处理函数
    processSymbol, err := p.Lookup("ProcessData")
    if err != nil {
        log.Fatalf("Failed to find ProcessData symbol: %v", err)
    }

    processFunc, ok := processSymbol.(func([]int) []int)
    if !ok {
        log.Fatalf("ProcessData symbol is not a function")
    }

    // 使用数据处理函数
    data := []int{1, 2, 3, 4, 5}
    processed := processFunc(data)
    fmt.Printf("Processed data: %v -> %v\n", data, processed)

    // 查找清理函数
    cleanupSymbol, err := p.Lookup("Cleanup")
    if err != nil {
        log.Fatalf("Failed to find Cleanup symbol: %v", err)
    }

    cleanupFunc, ok := cleanupSymbol.(func() error)
    if !ok {
        log.Fatalf("Cleanup symbol is not a function")
    }

    // 清理插件
    if err := cleanupFunc(); err != nil {
        log.Printf("Error during plugin cleanup: %v", err)
    }
}

高级插件加载器 #

插件加载器实现 #

package loader

import (
    "fmt"
    "os"
    "path/filepath"
    "plugin"
    "reflect"
    "sync"
    "time"
)

// PluginLoader 插件加载器
type PluginLoader struct {
    plugins     map[string]*LoadedPlugin
    pluginDir   string
    mutex       sync.RWMutex
    watcher     *FileWatcher
    autoReload  bool
}

// LoadedPlugin 已加载的插件
type LoadedPlugin struct {
    Name        string
    Path        string
    Plugin      *plugin.Plugin
    Info        interface{}
    LoadTime    time.Time
    LastModTime time.Time
    Symbols     map[string]interface{}
    RefCount    int
}

// PluginInterface 标准插件接口
type PluginInterface interface {
    Initialize() error
    Cleanup() error
    GetInfo() interface{}
}

// NewPluginLoader 创建插件加载器
func NewPluginLoader(pluginDir string, autoReload bool) *PluginLoader {
    loader := &PluginLoader{
        plugins:    make(map[string]*LoadedPlugin),
        pluginDir:  pluginDir,
        autoReload: autoReload,
    }

    if autoReload {
        loader.watcher = NewFileWatcher(pluginDir, loader.onFileChanged)
        loader.watcher.Start()
    }

    return loader
}

// LoadPlugin 加载插件
func (pl *PluginLoader) LoadPlugin(name string) (*LoadedPlugin, error) {
    pl.mutex.Lock()
    defer pl.mutex.Unlock()

    // 检查是否已加载
    if existing, exists := pl.plugins[name]; exists {
        existing.RefCount++
        return existing, nil
    }

    // 构建插件路径
    pluginPath := filepath.Join(pl.pluginDir, name+".so")

    // 检查文件是否存在
    if _, err := os.Stat(pluginPath); os.IsNotExist(err) {
        return nil, fmt.Errorf("plugin file not found: %s", pluginPath)
    }

    // 获取文件修改时间
    fileInfo, err := os.Stat(pluginPath)
    if err != nil {
        return nil, fmt.Errorf("failed to get file info: %v", err)
    }

    // 加载插件
    p, err := plugin.Open(pluginPath)
    if err != nil {
        return nil, fmt.Errorf("failed to load plugin: %v", err)
    }

    // 创建加载的插件对象
    loadedPlugin := &LoadedPlugin{
        Name:        name,
        Path:        pluginPath,
        Plugin:      p,
        LoadTime:    time.Now(),
        LastModTime: fileInfo.ModTime(),
        Symbols:     make(map[string]interface{}),
        RefCount:    1,
    }

    // 查找标准符号
    if err := pl.loadStandardSymbols(loadedPlugin); err != nil {
        return nil, fmt.Errorf("failed to load standard symbols: %v", err)
    }

    // 初始化插件
    if initFunc, exists := loadedPlugin.Symbols["Initialize"]; exists {
        if init, ok := initFunc.(func() error); ok {
            if err := init(); err != nil {
                return nil, fmt.Errorf("plugin initialization failed: %v", err)
            }
        }
    }

    pl.plugins[name] = loadedPlugin
    return loadedPlugin, nil
}

// loadStandardSymbols 加载标准符号
func (pl *PluginLoader) loadStandardSymbols(loadedPlugin *LoadedPlugin) error {
    standardSymbols := []string{"Initialize", "Cleanup", "Info"}

    for _, symbolName := range standardSymbols {
        symbol, err := loadedPlugin.Plugin.Lookup(symbolName)
        if err != nil {
            // 某些符号可能是可选的
            continue
        }
        loadedPlugin.Symbols[symbolName] = symbol
    }

    // 获取插件信息
    if infoSymbol, exists := loadedPlugin.Symbols["Info"]; exists {
        loadedPlugin.Info = infoSymbol
    }

    return nil
}

// UnloadPlugin 卸载插件
func (pl *PluginLoader) UnloadPlugin(name string) error {
    pl.mutex.Lock()
    defer pl.mutex.Unlock()

    loadedPlugin, exists := pl.plugins[name]
    if !exists {
        return fmt.Errorf("plugin %s not loaded", name)
    }

    loadedPlugin.RefCount--
    if loadedPlugin.RefCount > 0 {
        return nil // 仍有引用,不卸载
    }

    // 清理插件
    if cleanupFunc, exists := loadedPlugin.Symbols["Cleanup"]; exists {
        if cleanup, ok := cleanupFunc.(func() error); ok {
            if err := cleanup(); err != nil {
                return fmt.Errorf("plugin cleanup failed: %v", err)
            }
        }
    }

    delete(pl.plugins, name)
    return nil
}

// ReloadPlugin 重新加载插件
func (pl *PluginLoader) ReloadPlugin(name string) (*LoadedPlugin, error) {
    pl.mutex.Lock()
    defer pl.mutex.Unlock()

    // 先卸载现有插件
    if existing, exists := pl.plugins[name]; exists {
        // 强制卸载
        existing.RefCount = 1
        pl.mutex.Unlock()
        pl.UnloadPlugin(name)
        pl.mutex.Lock()
    }

    // 重新加载
    pl.mutex.Unlock()
    return pl.LoadPlugin(name)
}

// GetPlugin 获取已加载的插件
func (pl *PluginLoader) GetPlugin(name string) (*LoadedPlugin, error) {
    pl.mutex.RLock()
    defer pl.mutex.RUnlock()

    plugin, exists := pl.plugins[name]
    if !exists {
        return nil, fmt.Errorf("plugin %s not loaded", name)
    }

    return plugin, nil
}

// ListPlugins 列出所有已加载的插件
func (pl *PluginLoader) ListPlugins() []string {
    pl.mutex.RLock()
    defer pl.mutex.RUnlock()

    names := make([]string, 0, len(pl.plugins))
    for name := range pl.plugins {
        names = append(names, name)
    }

    return names
}

// ScanPluginDirectory 扫描插件目录
func (pl *PluginLoader) ScanPluginDirectory() ([]string, error) {
    files, err := filepath.Glob(filepath.Join(pl.pluginDir, "*.so"))
    if err != nil {
        return nil, err
    }

    plugins := make([]string, 0, len(files))
    for _, file := range files {
        name := filepath.Base(file)
        name = name[:len(name)-3] // 移除 .so 扩展名
        plugins = append(plugins, name)
    }

    return plugins, nil
}

// onFileChanged 文件变化回调
func (pl *PluginLoader) onFileChanged(path string, event FileEvent) {
    if !pl.autoReload {
        return
    }

    if filepath.Ext(path) != ".so" {
        return
    }

    name := filepath.Base(path)
    name = name[:len(name)-3] // 移除 .so 扩展名

    switch event {
    case FileEventModified:
        fmt.Printf("Plugin file modified: %s, reloading...\n", name)
        if _, err := pl.ReloadPlugin(name); err != nil {
            fmt.Printf("Failed to reload plugin %s: %v\n", name, err)
        } else {
            fmt.Printf("Plugin %s reloaded successfully\n", name)
        }

    case FileEventDeleted:
        fmt.Printf("Plugin file deleted: %s, unloading...\n", name)
        if err := pl.UnloadPlugin(name); err != nil {
            fmt.Printf("Failed to unload plugin %s: %v\n", name, err)
        } else {
            fmt.Printf("Plugin %s unloaded successfully\n", name)
        }
    }
}

// Close 关闭插件加载器
func (pl *PluginLoader) Close() error {
    if pl.watcher != nil {
        pl.watcher.Stop()
    }

    // 卸载所有插件
    pl.mutex.Lock()
    defer pl.mutex.Unlock()

    for name := range pl.plugins {
        pl.plugins[name].RefCount = 1
        pl.mutex.Unlock()
        pl.UnloadPlugin(name)
        pl.mutex.Lock()
    }

    return nil
}

// GetSymbol 获取插件符号
func (lp *LoadedPlugin) GetSymbol(name string) (interface{}, error) {
    if symbol, exists := lp.Symbols[name]; exists {
        return symbol, nil
    }

    symbol, err := lp.Plugin.Lookup(name)
    if err != nil {
        return nil, err
    }

    lp.Symbols[name] = symbol
    return symbol, nil
}

// CallFunction 调用插件函数
func (lp *LoadedPlugin) CallFunction(name string, args ...interface{}) ([]reflect.Value, error) {
    symbol, err := lp.GetSymbol(name)
    if err != nil {
        return nil, err
    }

    fn := reflect.ValueOf(symbol)
    if fn.Kind() != reflect.Func {
        return nil, fmt.Errorf("symbol %s is not a function", name)
    }

    // 准备参数
    argValues := make([]reflect.Value, len(args))
    for i, arg := range args {
        argValues[i] = reflect.ValueOf(arg)
    }

    // 调用函数
    results := fn.Call(argValues)
    return results, nil
}

文件监控系统 #

文件监控器实现 #

package loader

import (
    "os"
    "path/filepath"
    "sync"
    "time"
)

// FileEvent 文件事件类型
type FileEvent int

const (
    FileEventCreated FileEvent = iota
    FileEventModified
    FileEventDeleted
)

// FileWatcher 文件监控器
type FileWatcher struct {
    directory string
    callback  func(string, FileEvent)
    files     map[string]time.Time
    mutex     sync.RWMutex
    stopChan  chan struct{}
    running   bool
}

// NewFileWatcher 创建文件监控器
func NewFileWatcher(directory string, callback func(string, FileEvent)) *FileWatcher {
    return &FileWatcher{
        directory: directory,
        callback:  callback,
        files:     make(map[string]time.Time),
        stopChan:  make(chan struct{}),
    }
}

// Start 启动文件监控
func (fw *FileWatcher) Start() error {
    fw.mutex.Lock()
    defer fw.mutex.Unlock()

    if fw.running {
        return fmt.Errorf("file watcher already running")
    }

    // 初始扫描
    if err := fw.scanDirectory(); err != nil {
        return err
    }

    fw.running = true
    go fw.watchLoop()

    return nil
}

// Stop 停止文件监控
func (fw *FileWatcher) Stop() {
    fw.mutex.Lock()
    defer fw.mutex.Unlock()

    if !fw.running {
        return
    }

    fw.running = false
    close(fw.stopChan)
}

// watchLoop 监控循环
func (fw *FileWatcher) watchLoop() {
    ticker := time.NewTicker(1 * time.Second)
    defer ticker.Stop()

    for {
        select {
        case <-fw.stopChan:
            return
        case <-ticker.C:
            fw.checkChanges()
        }
    }
}

// scanDirectory 扫描目录
func (fw *FileWatcher) scanDirectory() error {
    return filepath.Walk(fw.directory, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }

        if !info.IsDir() && filepath.Ext(path) == ".so" {
            fw.files[path] = info.ModTime()
        }

        return nil
    })
}

// checkChanges 检查文件变化
func (fw *FileWatcher) checkChanges() {
    fw.mutex.Lock()
    defer fw.mutex.Unlock()

    // 扫描当前文件
    currentFiles := make(map[string]time.Time)
    filepath.Walk(fw.directory, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return nil
        }

        if !info.IsDir() && filepath.Ext(path) == ".so" {
            currentFiles[path] = info.ModTime()
        }

        return nil
    })

    // 检查新文件和修改的文件
    for path, modTime := range currentFiles {
        if lastModTime, exists := fw.files[path]; exists {
            if modTime.After(lastModTime) {
                // 文件被修改
                fw.callback(path, FileEventModified)
            }
        } else {
            // 新文件
            fw.callback(path, FileEventCreated)
        }
    }

    // 检查删除的文件
    for path := range fw.files {
        if _, exists := currentFiles[path]; !exists {
            // 文件被删除
            fw.callback(path, FileEventDeleted)
        }
    }

    // 更新文件列表
    fw.files = currentFiles
}

插件热更新系统 #

热更新管理器 #

package hotreload

import (
    "context"
    "fmt"
    "sync"
    "time"

    "your-project/loader"
    "your-project/plugin"
)

// HotReloadManager 热更新管理器
type HotReloadManager struct {
    pluginManager *plugin.PluginManager
    pluginLoader  *loader.PluginLoader
    updateQueue   chan UpdateRequest
    mutex         sync.RWMutex
    running       bool
    stopChan      chan struct{}
}

// UpdateRequest 更新请求
type UpdateRequest struct {
    PluginName string
    Action     UpdateAction
    Config     map[string]interface{}
    Response   chan UpdateResponse
}

// UpdateAction 更新动作
type UpdateAction int

const (
    ActionLoad UpdateAction = iota
    ActionUnload
    ActionReload
    ActionUpdate
)

// UpdateResponse 更新响应
type UpdateResponse struct {
    Success bool
    Error   error
    Message string
}

// NewHotReloadManager 创建热更新管理器
func NewHotReloadManager(pluginManager *plugin.PluginManager, pluginLoader *loader.PluginLoader) *HotReloadManager {
    return &HotReloadManager{
        pluginManager: pluginManager,
        pluginLoader:  pluginLoader,
        updateQueue:   make(chan UpdateRequest, 100),
        stopChan:      make(chan struct{}),
    }
}

// Start 启动热更新管理器
func (hrm *HotReloadManager) Start() error {
    hrm.mutex.Lock()
    defer hrm.mutex.Unlock()

    if hrm.running {
        return fmt.Errorf("hot reload manager already running")
    }

    hrm.running = true
    go hrm.processUpdates()

    return nil
}

// Stop 停止热更新管理器
func (hrm *HotReloadManager) Stop() error {
    hrm.mutex.Lock()
    defer hrm.mutex.Unlock()

    if !hrm.running {
        return fmt.Errorf("hot reload manager not running")
    }

    hrm.running = false
    close(hrm.stopChan)

    return nil
}

// RequestUpdate 请求更新
func (hrm *HotReloadManager) RequestUpdate(pluginName string, action UpdateAction, config map[string]interface{}) UpdateResponse {
    if !hrm.running {
        return UpdateResponse{
            Success: false,
            Error:   fmt.Errorf("hot reload manager not running"),
        }
    }

    responseChan := make(chan UpdateResponse, 1)
    request := UpdateRequest{
        PluginName: pluginName,
        Action:     action,
        Config:     config,
        Response:   responseChan,
    }

    select {
    case hrm.updateQueue <- request:
        return <-responseChan
    case <-time.After(30 * time.Second):
        return UpdateResponse{
            Success: false,
            Error:   fmt.Errorf("update request timeout"),
        }
    }
}

// processUpdates 处理更新请求
func (hrm *HotReloadManager) processUpdates() {
    for {
        select {
        case <-hrm.stopChan:
            return
        case request := <-hrm.updateQueue:
            response := hrm.handleUpdateRequest(request)
            request.Response <- response
        }
    }
}

// handleUpdateRequest 处理更新请求
func (hrm *HotReloadManager) handleUpdateRequest(request UpdateRequest) UpdateResponse {
    switch request.Action {
    case ActionLoad:
        return hrm.handleLoad(request.PluginName, request.Config)
    case ActionUnload:
        return hrm.handleUnload(request.PluginName)
    case ActionReload:
        return hrm.handleReload(request.PluginName, request.Config)
    case ActionUpdate:
        return hrm.handleUpdate(request.PluginName, request.Config)
    default:
        return UpdateResponse{
            Success: false,
            Error:   fmt.Errorf("unknown update action"),
        }
    }
}

// handleLoad 处理加载请求
func (hrm *HotReloadManager) handleLoad(pluginName string, config map[string]interface{}) UpdateResponse {
    // 从文件系统加载插件
    loadedPlugin, err := hrm.pluginLoader.LoadPlugin(pluginName)
    if err != nil {
        return UpdateResponse{
            Success: false,
            Error:   fmt.Errorf("failed to load plugin from file: %v", err),
        }
    }

    // 创建插件包装器
    wrapper := NewPluginWrapper(loadedPlugin)

    // 在插件管理器中注册插件
    if err := hrm.pluginManager.LoadPlugin(pluginName, wrapper, config); err != nil {
        hrm.pluginLoader.UnloadPlugin(pluginName)
        return UpdateResponse{
            Success: false,
            Error:   fmt.Errorf("failed to register plugin: %v", err),
        }
    }

    // 启动插件
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    if err := hrm.pluginManager.StartPlugin(ctx, pluginName); err != nil {
        hrm.pluginManager.UnloadPlugin(pluginName)
        hrm.pluginLoader.UnloadPlugin(pluginName)
        return UpdateResponse{
            Success: false,
            Error:   fmt.Errorf("failed to start plugin: %v", err),
        }
    }

    return UpdateResponse{
        Success: true,
        Message: fmt.Sprintf("Plugin %s loaded and started successfully", pluginName),
    }
}

// handleUnload 处理卸载请求
func (hrm *HotReloadManager) handleUnload(pluginName string) UpdateResponse {
    // 从插件管理器中卸载
    if err := hrm.pluginManager.UnloadPlugin(pluginName); err != nil {
        return UpdateResponse{
            Success: false,
            Error:   fmt.Errorf("failed to unload plugin from manager: %v", err),
        }
    }

    // 从加载器中卸载
    if err := hrm.pluginLoader.UnloadPlugin(pluginName); err != nil {
        return UpdateResponse{
            Success: false,
            Error:   fmt.Errorf("failed to unload plugin from loader: %v", err),
        }
    }

    return UpdateResponse{
        Success: true,
        Message: fmt.Sprintf("Plugin %s unloaded successfully", pluginName),
    }
}

// handleReload 处理重新加载请求
func (hrm *HotReloadManager) handleReload(pluginName string, config map[string]interface{}) UpdateResponse {
    // 先卸载
    unloadResponse := hrm.handleUnload(pluginName)
    if !unloadResponse.Success {
        return unloadResponse
    }

    // 等待一小段时间确保资源完全释放
    time.Sleep(100 * time.Millisecond)

    // 重新加载
    return hrm.handleLoad(pluginName, config)
}

// handleUpdate 处理配置更新请求
func (hrm *HotReloadManager) handleUpdate(pluginName string, config map[string]interface{}) UpdateResponse {
    // 获取插件
    pluginInstance, err := hrm.pluginManager.GetPlugin(pluginName)
    if err != nil {
        return UpdateResponse{
            Success: false,
            Error:   fmt.Errorf("plugin not found: %v", err),
        }
    }

    // 检查插件是否支持配置更新
    if configurable, ok := pluginInstance.(plugin.ConfigurablePlugin); ok {
        if err := configurable.UpdateConfig(config); err != nil {
            return UpdateResponse{
                Success: false,
                Error:   fmt.Errorf("failed to update plugin config: %v", err),
            }
        }

        return UpdateResponse{
            Success: true,
            Message: fmt.Sprintf("Plugin %s configuration updated successfully", pluginName),
        }
    }

    return UpdateResponse{
        Success: false,
        Error:   fmt.Errorf("plugin does not support configuration updates"),
    }
}

插件包装器 #

package hotreload

import (
    "context"
    "fmt"
    "reflect"

    "your-project/loader"
    "your-project/plugin"
)

// PluginWrapper 插件包装器,将动态加载的插件适配到插件接口
type PluginWrapper struct {
    loadedPlugin *loader.LoadedPlugin
    info         plugin.PluginInfo
    status       plugin.PluginStatus
    config       map[string]interface{}
}

// NewPluginWrapper 创建插件包装器
func NewPluginWrapper(loadedPlugin *loader.LoadedPlugin) *PluginWrapper {
    wrapper := &PluginWrapper{
        loadedPlugin: loadedPlugin,
        status:       plugin.StatusUnloaded,
        config:       make(map[string]interface{}),
    }

    // 提取插件信息
    wrapper.extractPluginInfo()

    return wrapper
}

// extractPluginInfo 提取插件信息
func (pw *PluginWrapper) extractPluginInfo() {
    // 尝试从插件中获取信息
    if infoSymbol, err := pw.loadedPlugin.GetSymbol("Info"); err == nil {
        if info, ok := infoSymbol.(*plugin.PluginInfo); ok {
            pw.info = *info
            return
        }

        // 尝试其他可能的信息结构
        infoValue := reflect.ValueOf(infoSymbol)
        if infoValue.Kind() == reflect.Ptr {
            infoValue = infoValue.Elem()
        }

        if infoValue.Kind() == reflect.Struct {
            pw.info = plugin.PluginInfo{
                Name:        pw.getStringField(infoValue, "Name"),
                Version:     pw.getStringField(infoValue, "Version"),
                Description: pw.getStringField(infoValue, "Description"),
                Author:      pw.getStringField(infoValue, "Author"),
                License:     pw.getStringField(infoValue, "License"),
            }
            return
        }
    }

    // 使用默认信息
    pw.info = plugin.PluginInfo{
        Name:        pw.loadedPlugin.Name,
        Version:     "1.0.0",
        Description: "Dynamically loaded plugin",
        Author:      "Unknown",
        License:     "Unknown",
    }
}

// getStringField 获取结构体字符串字段
func (pw *PluginWrapper) getStringField(value reflect.Value, fieldName string) string {
    field := value.FieldByName(fieldName)
    if field.IsValid() && field.Kind() == reflect.String {
        return field.String()
    }
    return ""
}

// Info 获取插件信息
func (pw *PluginWrapper) Info() plugin.PluginInfo {
    return pw.info
}

// Initialize 初始化插件
func (pw *PluginWrapper) Initialize(ctx context.Context, config map[string]interface{}) error {
    if pw.status != plugin.StatusUnloaded {
        return fmt.Errorf("plugin already initialized")
    }

    pw.config = config

    // 调用插件的初始化函数
    if results, err := pw.loadedPlugin.CallFunction("Initialize"); err == nil {
        if len(results) > 0 && !results[0].IsNil() {
            if err, ok := results[0].Interface().(error); ok {
                return err
            }
        }
    }

    pw.status = plugin.StatusLoaded
    return nil
}

// Start 启动插件
func (pw *PluginWrapper) Start(ctx context.Context) error {
    if pw.status != plugin.StatusLoaded {
        return fmt.Errorf("plugin not loaded or already started")
    }

    // 调用插件的启动函数(如果存在)
    if results, err := pw.loadedPlugin.CallFunction("Start"); err == nil {
        if len(results) > 0 && !results[0].IsNil() {
            if err, ok := results[0].Interface().(error); ok {
                return err
            }
        }
    }

    pw.status = plugin.StatusActive
    return nil
}

// Stop 停止插件
func (pw *PluginWrapper) Stop(ctx context.Context) error {
    if pw.status != plugin.StatusActive {
        return fmt.Errorf("plugin not active")
    }

    // 调用插件的停止函数(如果存在)
    if results, err := pw.loadedPlugin.CallFunction("Stop"); err == nil {
        if len(results) > 0 && !results[0].IsNil() {
            if err, ok := results[0].Interface().(error); ok {
                return err
            }
        }
    }

    pw.status = plugin.StatusLoaded
    return nil
}

// Cleanup 清理插件资源
func (pw *PluginWrapper) Cleanup() error {
    // 调用插件的清理函数
    if results, err := pw.loadedPlugin.CallFunction("Cleanup"); err == nil {
        if len(results) > 0 && !results[0].IsNil() {
            if err, ok := results[0].Interface().(error); ok {
                return err
            }
        }
    }

    pw.status = plugin.StatusUnloaded
    pw.config = make(map[string]interface{})
    return nil
}

// Status 获取插件状态
func (pw *PluginWrapper) Status() plugin.PluginStatus {
    return pw.status
}

// HealthCheck 健康检查
func (pw *PluginWrapper) HealthCheck() error {
    if pw.status == plugin.StatusError {
        return fmt.Errorf("plugin is in error state")
    }

    // 调用插件的健康检查函数(如果存在)
    if results, err := pw.loadedPlugin.CallFunction("HealthCheck"); err == nil {
        if len(results) > 0 && !results[0].IsNil() {
            if err, ok := results[0].Interface().(error); ok {
                return err
            }
        }
    }

    return nil
}

// GetConfig 获取配置
func (pw *PluginWrapper) GetConfig() map[string]interface{} {
    result := make(map[string]interface{})
    for k, v := range pw.config {
        result[k] = v
    }
    return result
}

// UpdateConfig 更新配置
func (pw *PluginWrapper) UpdateConfig(config map[string]interface{}) error {
    for k, v := range config {
        pw.config[k] = v
    }

    // 调用插件的配置更新函数(如果存在)
    if results, err := pw.loadedPlugin.CallFunction("UpdateConfig", config); err == nil {
        if len(results) > 0 && !results[0].IsNil() {
            if err, ok := results[0].Interface().(error); ok {
                return err
            }
        }
    }

    return nil
}

// ConfigSchema 配置模式
func (pw *PluginWrapper) ConfigSchema() map[string]interface{} {
    // 调用插件的配置模式函数(如果存在)
    if results, err := pw.loadedPlugin.CallFunction("ConfigSchema"); err == nil {
        if len(results) > 0 && !results[0].IsNil() {
            if schema, ok := results[0].Interface().(map[string]interface{}); ok {
                return schema
            }
        }
    }

    return make(map[string]interface{})
}

// HandleEvent 处理事件
func (pw *PluginWrapper) HandleEvent(ctx context.Context, event plugin.Event) error {
    if pw.status != plugin.StatusActive {
        return fmt.Errorf("plugin not active")
    }

    // 调用插件的事件处理函数(如果存在)
    if results, err := pw.loadedPlugin.CallFunction("HandleEvent", ctx, event); err == nil {
        if len(results) > 0 && !results[0].IsNil() {
            if err, ok := results[0].Interface().(error); ok {
                return err
            }
        }
    }

    return nil
}

// SupportedEvents 支持的事件类型
func (pw *PluginWrapper) SupportedEvents() []string {
    // 调用插件的支持事件函数(如果存在)
    if results, err := pw.loadedPlugin.CallFunction("SupportedEvents"); err == nil {
        if len(results) > 0 && !results[0].IsNil() {
            if events, ok := results[0].Interface().([]string); ok {
                return events
            }
        }
    }

    return []string{}
}

完整使用示例 #

主程序示例 #

package main

import (
    "context"
    "fmt"
    "log"
    "os"
    "os/signal"
    "syscall"
    "time"

    "your-project/hotreload"
    "your-project/loader"
    "your-project/plugin"
)

func main() {
    // 创建插件目录
    pluginDir := "./plugins"
    if err := os.MkdirAll(pluginDir, 0755); err != nil {
        log.Fatalf("Failed to create plugin directory: %v", err)
    }

    // 创建插件管理器
    pluginManager := plugin.NewPluginManager(plugin.DefaultManagerConfig())
    if err := pluginManager.Start(context.Background()); err != nil {
        log.Fatalf("Failed to start plugin manager: %v", err)
    }
    defer pluginManager.Stop(context.Background())

    // 创建插件加载器
    pluginLoader := loader.NewPluginLoader(pluginDir, true)
    defer pluginLoader.Close()

    // 创建热更新管理器
    hotReloadManager := hotreload.NewHotReloadManager(pluginManager, pluginLoader)
    if err := hotReloadManager.Start(); err != nil {
        log.Fatalf("Failed to start hot reload manager: %v", err)
    }
    defer hotReloadManager.Stop()

    // 订阅插件事件
    pluginManager.SubscribeToEvents("plugin.loaded", func(event plugin.Event) {
        fmt.Printf("Plugin loaded: %s\n", event.Data["plugin_name"])
    })

    pluginManager.SubscribeToEvents("plugin.started", func(event plugin.Event) {
        fmt.Printf("Plugin started: %s\n", event.Data["plugin_name"])
    })

    pluginManager.SubscribeToEvents("plugin.restarted", func(event plugin.Event) {
        fmt.Printf("Plugin restarted: %s (attempt %v)\n",
                   event.Data["plugin_name"], event.Data["attempt"])
    })

    // 扫描并加载现有插件
    availablePlugins, err := pluginLoader.ScanPluginDirectory()
    if err != nil {
        log.Printf("Failed to scan plugin directory: %v", err)
    } else {
        fmt.Printf("Found %d plugins: %v\n", len(availablePlugins), availablePlugins)

        // 自动加载所有可用插件
        for _, pluginName := range availablePlugins {
            config := map[string]interface{}{
                "auto_loaded": true,
                "load_time":   time.Now().Unix(),
            }

            response := hotReloadManager.RequestUpdate(pluginName, hotreload.ActionLoad, config)
            if response.Success {
                fmt.Printf("Auto-loaded plugin: %s\n", pluginName)
            } else {
                fmt.Printf("Failed to auto-load plugin %s: %v\n", pluginName, response.Error)
            }
        }
    }

    // 启动状态监控
    go func() {
        ticker := time.NewTicker(30 * time.Second)
        defer ticker.Stop()

        for {
            select {
            case <-ticker.C:
                fmt.Println("\n=== Plugin Status ===")
                plugins := pluginManager.ListPlugins()
                for _, name := range plugins {
                    status, _ := pluginManager.GetPluginStatus(name)
                    info, _ := pluginManager.GetPluginInfo(name)
                    fmt.Printf("Plugin: %s (v%s) - Status: %s\n",
                               info.Name, info.Version, status)
                }
                fmt.Println("====================\n")
            }
        }
    }()

    // 演示热更新功能
    go func() {
        time.Sleep(10 * time.Second)

        // 演示配置更新
        fmt.Println("Demonstrating configuration update...")
        for _, pluginName := range pluginManager.ListPlugins() {
            newConfig := map[string]interface{}{
                "updated_at": time.Now().Unix(),
                "demo_mode":  true,
            }

            response := hotReloadManager.RequestUpdate(pluginName, hotreload.ActionUpdate, newConfig)
            if response.Success {
                fmt.Printf("Updated config for plugin: %s\n", pluginName)
            } else {
                fmt.Printf("Failed to update config for plugin %s: %v\n", pluginName, response.Error)
            }
        }
    }()

    // 等待中断信号
    sigChan := make(chan os.Signal, 1)
    signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

    fmt.Println("Hot reload plugin system started.")
    fmt.Printf("Plugin directory: %s\n", pluginDir)
    fmt.Println("Add .so files to the plugin directory to see hot loading in action.")
    fmt.Println("Press Ctrl+C to stop.")

    <-sigChan
    fmt.Println("\nShutting down hot reload plugin system...")
}

编译脚本示例 #

#!/bin/bash

# build_plugins.sh - 编译插件脚本

PLUGIN_DIR="./plugins"
SOURCE_DIR="./plugin_sources"

# 创建插件目录
mkdir -p $PLUGIN_DIR

# 编译所有插件
for plugin_file in $SOURCE_DIR/*.go; do
    if [ -f "$plugin_file" ]; then
        plugin_name=$(basename "$plugin_file" .go)
        echo "Building plugin: $plugin_name"

        go build -buildmode=plugin -o "$PLUGIN_DIR/$plugin_name.so" "$plugin_file"

        if [ $? -eq 0 ]; then
            echo "Successfully built: $plugin_name.so"
        else
            echo "Failed to build: $plugin_name"
        fi
    fi
done

echo "Plugin build completed."

小结 #

本节详细介绍了插件动态加载的实现方法,包括:

  1. Go Plugin 包:使用 Go 内置的 plugin 包进行基础插件加载
  2. 高级插件加载器:实现了功能完整的插件加载器,支持符号查找和函数调用
  3. 文件监控系统:实现了文件变化监控,支持自动重新加载
  4. 热更新系统:提供了完整的热更新管理功能
  5. 插件包装器:将动态加载的插件适配到标准插件接口

动态加载为插件系统提供了极大的灵活性,但也带来了复杂性。在实际应用中,需要仔细考虑版本兼容性、错误处理、资源管理等问题。通过本章的学习,你应该能够设计和实现一个完整的插件系统,为应用程序提供强大的扩展能力。