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."
小结 #
本节详细介绍了插件动态加载的实现方法,包括:
- Go Plugin 包:使用 Go 内置的 plugin 包进行基础插件加载
- 高级插件加载器:实现了功能完整的插件加载器,支持符号查找和函数调用
- 文件监控系统:实现了文件变化监控,支持自动重新加载
- 热更新系统:提供了完整的热更新管理功能
- 插件包装器:将动态加载的插件适配到标准插件接口
动态加载为插件系统提供了极大的灵活性,但也带来了复杂性。在实际应用中,需要仔细考虑版本兼容性、错误处理、资源管理等问题。通过本章的学习,你应该能够设计和实现一个完整的插件系统,为应用程序提供强大的扩展能力。