diff --git a/server/router/example/plugin.go b/server/router/example/plugin.go new file mode 100644 index 00000000..c2b61fef --- /dev/null +++ b/server/router/example/plugin.go @@ -0,0 +1,16 @@ +package example + +import "github.com/gin-gonic/gin" + +type Plugin struct { +} + +func (*Plugin) Register(group *gin.RouterGroup) { + group.GET("hello", func(context *gin.Context) { + context.JSON(200, "hello world") + }) +} + +func (*Plugin) RouterPath() string { + return "group" +} diff --git a/server/utils/plugin/plugin.go b/server/utils/plugin/plugin.go new file mode 100644 index 00000000..f5e7a536 --- /dev/null +++ b/server/utils/plugin/plugin.go @@ -0,0 +1,41 @@ +package plugin + +import ( + "plugin" + "sync" + + "github.com/gin-gonic/gin" +) + +const ( + OnlyFuncName = "Plugin" +) + +var ManagementPlugin = managementPlugin{mp: make(map[string]*plugin.Plugin)} + +type managementPlugin struct { + mp map[string]*plugin.Plugin + sync.Mutex +} + +func (m *managementPlugin) SetPlugin(key string, p *plugin.Plugin) { + m.Lock() + defer m.Unlock() + m.mp[key] = p +} + +func (m *managementPlugin) GetPlugin(key string) (p *plugin.Plugin, ok bool) { + m.Lock() + defer m.Unlock() + p, ok = m.mp[key] + return +} + +// Plugin 插件模式接口化 +type Plugin interface { + // Register 注册路由 + Register(group *gin.RouterGroup) + + // RouterPath 用户返回注册路由 + RouterPath() string +} diff --git a/server/utils/plugin/plugin_uinx.go b/server/utils/plugin/plugin_uinx.go new file mode 100644 index 00000000..69a9d1b6 --- /dev/null +++ b/server/utils/plugin/plugin_uinx.go @@ -0,0 +1,69 @@ +//+build !windows + +package plugin + +import ( + "errors" + "fmt" + "io/fs" + "io/ioutil" + "os" + "path/filepath" + "plugin" +) + +// LoadPlugin 加载插件 传入path +func LoadPlugin(path string) error { + path, err := filepath.Abs(path) + if err != nil { + return err + } + fileInfo, err := os.Stat(path) + if err != nil { + return err + } + if fileInfo.IsDir() { + fileSlice, err := ioutil.ReadDir(path) + if err != nil { + return err + } + for _, ff := range fileSlice { + if !ff.IsDir() && filepath.Ext(ff.Name()) == ".so" { + if err = loadPlugin(path, ff); err != nil { + return err + } + } else if ff.IsDir() { + _ = LoadPlugin(filepath.Join(path, ff.Name())) + } + } + return nil + } else { + return loadPlugin(path, fileInfo) + } +} + +func loadPlugin(path string, f fs.FileInfo) error { + if filepath.Ext(f.Name()) == ".so" { + fPath := filepath.Join(path, f.Name()) + // 加载插件 + p, err := plugin.Open(fPath) + if err != nil { + fmt.Println("loadPlugin err ", err) + return err + } + // 判断是否满足协议 + // 要满足 OnlyFuncName && 实现 Plugin 接口 + if v, err := p.Lookup(OnlyFuncName); err != nil { + fmt.Println("loadPlugin err ", err) + return err + } else if _, ok := v.(Plugin); !ok { + fmt.Println("loadPlugin err ", fmt.Sprintf("path:%s 没有实现 %s 接口", filepath.Base(fPath), OnlyFuncName)) + return errors.New("没有实现指定接口") + } else { + + } + fmt.Println("loadPlugin add ", filepath.Base(fPath)) + ManagementPlugin.SetPlugin(filepath.Base(fPath), p) + } + return nil +}