Some checks failed
Go / build (.exe, 386, windows, windows-386) (push) Has been cancelled
Go / build (.exe, amd64, windows, windows-amd64) (push) Has been cancelled
Go / build (.exe, arm64, windows, windows-arm64) (push) Has been cancelled
Go / build (386, freebsd, freebsd-386) (push) Has been cancelled
Go / build (386, linux, linux-386) (push) Has been cancelled
Go / build (386, netbsd, netbsd-386) (push) Has been cancelled
Go / build (386, openbsd, openbsd-386) (push) Has been cancelled
Go / build (386, plan9, plan9-386) (push) Has been cancelled
Go / build (amd64, darwin, darwin-amd64) (push) Has been cancelled
Go / build (amd64, dragonfly, dragonfly-amd64) (push) Has been cancelled
Go / build (amd64, freebsd, freebsd-amd64) (push) Has been cancelled
Go / build (amd64, illumos, illumos-amd64) (push) Has been cancelled
Go / build (amd64, linux, linux-amd64) (push) Has been cancelled
Go / build (amd64, netbsd, netbsd-amd64) (push) Has been cancelled
Go / build (amd64, openbsd, openbsd-amd64) (push) Has been cancelled
Go / build (amd64, plan9, plan9-amd64) (push) Has been cancelled
Go / build (amd64, solaris, solaris-amd64) (push) Has been cancelled
Go / build (arm, 6, linux, linux-armv6) (push) Has been cancelled
Go / build (arm, 7, linux, linux-armv7) (push) Has been cancelled
Go / build (arm, freebsd, freebsd-arm) (push) Has been cancelled
Go / build (arm, netbsd, netbsd-arm) (push) Has been cancelled
Go / build (arm, openbsd, openbsd-arm) (push) Has been cancelled
Go / build (arm, plan9, plan9-arm) (push) Has been cancelled
Go / build (arm64, darwin, darwin-arm64) (push) Has been cancelled
Go / build (arm64, freebsd, freebsd-arm64) (push) Has been cancelled
Go / build (arm64, linux, linux-arm64) (push) Has been cancelled
Go / build (arm64, netbsd, netbsd-arm64) (push) Has been cancelled
Go / build (arm64, openbsd, openbsd-arm64) (push) Has been cancelled
Go / build (loong64, linux, linux-loong64) (push) Has been cancelled
Go / build (mips, linux, linux-mips) (push) Has been cancelled
Go / build (mips64, linux, linux-mips64) (push) Has been cancelled
Go / build (mips64le, linux, linux-mips64le) (push) Has been cancelled
Go / build (mipsle, linux, linux-mipsle) (push) Has been cancelled
Go / build (ppc64, aix, aix-ppc64) (push) Has been cancelled
Go / build (ppc64, linux, linux-ppc64) (push) Has been cancelled
Go / build (ppc64, openbsd, openbsd-ppc64) (push) Has been cancelled
Go / build (ppc64le, linux, linux-ppc64le) (push) Has been cancelled
Go / build (riscv64, freebsd, freebsd-riscv64) (push) Has been cancelled
Go / build (riscv64, linux, linux-riscv64) (push) Has been cancelled
Go / build (riscv64, openbsd, openbsd-riscv64) (push) Has been cancelled
Go / build (s390x, linux, linux-s390x) (push) Has been cancelled
Docker Image / docker (push) Has been cancelled
Go / merge-artifacts (push) Has been cancelled
256 lines
6.8 KiB
Go
256 lines
6.8 KiB
Go
// cmd/gateway/plugin.go 把网关运行时接入 pluginmanager,负责钩子分发和插件生命周期加载。
|
||
|
||
package main
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"net"
|
||
"plugin"
|
||
"sync"
|
||
"time"
|
||
|
||
"github.com/rs/zerolog/log"
|
||
"github.com/tursom/mc-gateway/plugin/api"
|
||
)
|
||
|
||
var (
|
||
exitWaitGroup sync.WaitGroup
|
||
|
||
pluginLock sync.RWMutex
|
||
plugins = make(map[string]api.Plugin)
|
||
|
||
hooks = make(map[string]map[string]any)
|
||
)
|
||
|
||
type (
|
||
Gateway struct {
|
||
pluginId string
|
||
}
|
||
)
|
||
|
||
func loadPlugins() {
|
||
pluginLock.Lock()
|
||
defer pluginLock.Unlock()
|
||
|
||
for pluginKey, pluginConfig := range config.Plugin {
|
||
if enable, ok := pluginConfig["enable"].(bool); ok && enable {
|
||
pluginFile := pluginKey
|
||
if file, ok := pluginConfig["file"].(string); ok {
|
||
pluginFile = file
|
||
}
|
||
|
||
if _, ok := plugins[pluginKey]; ok {
|
||
continue
|
||
}
|
||
|
||
gateway := &Gateway{
|
||
pluginId: pluginKey,
|
||
}
|
||
|
||
log.Info().Str("plugin", pluginKey).Msg("Loading plugin")
|
||
p, err := plugin.Open(pluginFile + ".so")
|
||
if err != nil {
|
||
log.Err(err).Str("plugin", pluginKey).Msg("Failed to open plugin")
|
||
continue
|
||
}
|
||
|
||
pluginSymbol, err := p.Lookup("Plugin")
|
||
if err != nil {
|
||
log.Err(err).Str("plugin", pluginKey).Msg("Failed to lookup plugin")
|
||
continue
|
||
}
|
||
pluginFactory, ok := pluginSymbol.(func() api.Plugin)
|
||
if !ok {
|
||
log.Err(err).Str("plugin", pluginKey).Msg("Invalid plugin factory signature")
|
||
continue
|
||
}
|
||
pluginInstance := pluginFactory()
|
||
cfgObj := pluginInstance.NewConfigObj()
|
||
if err := loadPluginConfig(pluginConfig, cfgObj); err != nil {
|
||
log.Err(err).Str("plugin", pluginKey).Msg("Failed to load plugin config")
|
||
continue
|
||
}
|
||
|
||
if err := pluginInstance.ReloadConfig(cfgObj); err != nil {
|
||
log.Err(err).Str("plugin", pluginKey).Msg("Failed to reload plugin config")
|
||
continue
|
||
}
|
||
|
||
if err := pluginInstance.Init(gateway); err != nil {
|
||
log.Err(err).Str("plugin", pluginKey).Msg("Failed to initialize plugin")
|
||
continue
|
||
}
|
||
|
||
plugins[pluginKey] = pluginInstance
|
||
hooks[pluginKey] = make(map[string]any)
|
||
log.Info().Str("plugin", pluginKey).Msg("Plugin loaded successfully")
|
||
} else {
|
||
if plugin, ok := plugins[pluginKey]; ok {
|
||
if err := plugin.Destroy(); err != nil {
|
||
log.Err(err).Str("plugin", pluginKey).Msg("Failed to destroy plugin")
|
||
}
|
||
log.Info().Str("plugin", pluginKey).Msg("Plugin disabled")
|
||
}
|
||
delete(plugins, pluginKey)
|
||
delete(hooks, pluginKey)
|
||
}
|
||
}
|
||
}
|
||
|
||
// HandleConn 实现 api.Gateway,用于让插件把连接交回网关主流程。
|
||
func (g *Gateway) HandleConn(conn net.Conn) {
|
||
go handleRequest(conn)
|
||
}
|
||
|
||
// Hook 实现 api.Gateway,用于注册旧版内存钩子处理器。
|
||
func (g *Gateway) Hook(hook string, handler any) error {
|
||
pluginLock.Lock()
|
||
defer pluginLock.Unlock()
|
||
|
||
hooks[g.pluginId][hook] = handler
|
||
return nil
|
||
}
|
||
|
||
// ExitWaitGroup 实现 api.Gateway,用于把插件后台任务纳入进程退出等待。
|
||
func (g *Gateway) ExitWaitGroup() *sync.WaitGroup {
|
||
return &exitWaitGroup
|
||
}
|
||
|
||
func (g *Gateway) EmitEvent(ctx context.Context, name string, fields map[string]string) error {
|
||
_ = ctx
|
||
_ = name
|
||
_ = fields
|
||
return nil
|
||
}
|
||
|
||
func (g *Gateway) ObserveMetric(ctx context.Context, name string, value float64, labels map[string]string) error {
|
||
_ = ctx
|
||
_ = name
|
||
_ = value
|
||
_ = labels
|
||
return nil
|
||
}
|
||
|
||
func (g *Gateway) Logger() api.Logger {
|
||
return noopPluginLogger{}
|
||
}
|
||
|
||
func (g *Gateway) DataStore() api.DataStore {
|
||
return noopPluginDataStore{}
|
||
}
|
||
|
||
func (g *Gateway) FileStore() api.FileStore {
|
||
return noopPluginFileStore{}
|
||
}
|
||
|
||
func (g *Gateway) ExternalClient(name string) api.ExternalClient {
|
||
_ = name
|
||
return noopExternalClient{}
|
||
}
|
||
|
||
func (g *Gateway) RegisterBackgroundTask(task api.BackgroundTask) error {
|
||
_ = task
|
||
return nil
|
||
}
|
||
|
||
// TestOp 实现 api.Gateway,保留给测试或调试插件能力探测。
|
||
func (g *Gateway) TestOp() {
|
||
panic("unimplemented")
|
||
}
|
||
|
||
type noopPluginLogger struct{}
|
||
|
||
func (noopPluginLogger) Debug(context.Context, string, map[string]string) {}
|
||
func (noopPluginLogger) Info(context.Context, string, map[string]string) {}
|
||
func (noopPluginLogger) Warn(context.Context, string, map[string]string) {}
|
||
func (noopPluginLogger) Error(context.Context, string, map[string]string) {}
|
||
|
||
type noopPluginDataStore struct{}
|
||
|
||
func (noopPluginDataStore) Put(context.Context, api.DataRecord) error { return nil }
|
||
func (noopPluginDataStore) Get(context.Context, string) (api.DataRecord, error) {
|
||
return api.DataRecord{}, errors.New("plugin data store is unavailable")
|
||
}
|
||
func (noopPluginDataStore) Delete(context.Context, string) error { return nil }
|
||
|
||
type noopPluginFileStore struct{}
|
||
|
||
func (noopPluginFileStore) ResourcePath(string) (string, error) {
|
||
return "", errors.New("plugin file store is unavailable")
|
||
}
|
||
func (noopPluginFileStore) Write(context.Context, string, string, []byte, string, time.Duration) error {
|
||
return nil
|
||
}
|
||
func (noopPluginFileStore) Read(context.Context, string, string, int64) ([]byte, error) {
|
||
return nil, errors.New("plugin file store is unavailable")
|
||
}
|
||
func (noopPluginFileStore) Delete(context.Context, string, string) error { return nil }
|
||
|
||
type noopExternalClient struct{}
|
||
|
||
func (noopExternalClient) DoHTTP(context.Context, api.ExternalRequest) (api.ExternalResponse, error) {
|
||
return api.ExternalResponse{}, errors.New("external client is unavailable")
|
||
}
|
||
func (noopExternalClient) DialTCP(context.Context, string, time.Duration) (net.Conn, error) {
|
||
return nil, errors.New("external client is unavailable")
|
||
}
|
||
func (noopExternalClient) HealthCheck(context.Context) error { return nil }
|
||
|
||
func Handler1[T1, R any](t1 T1) func(func(T1) R) R {
|
||
return func(acceptor func(T1) R) R {
|
||
return acceptor(t1)
|
||
}
|
||
}
|
||
|
||
func Handler2[T1, T2, R any](t1 T1, t2 T2) func(func(T1, T2) R) R {
|
||
return func(acceptor func(T1, T2) R) R {
|
||
return acceptor(t1, t2)
|
||
}
|
||
}
|
||
|
||
func Handler1R2[T1, R1, R2 any](t1 T1) func(func(T1) (R1, R2)) (R1, R2) {
|
||
return func(acceptor func(T1) (R1, R2)) (R1, R2) {
|
||
return acceptor(t1)
|
||
}
|
||
}
|
||
|
||
func invokeFirstHookHandler[Acceptor, Handler any](
|
||
hook api.HookType[Acceptor, Handler],
|
||
acceptor func(Acceptor) bool,
|
||
handelr func(Handler) error,
|
||
) (bool, error) {
|
||
pluginLock.RLock()
|
||
defer pluginLock.RUnlock()
|
||
|
||
for _, handlers := range hooks {
|
||
if handler, ok := handlers[hook.Key()].(api.HookHandler[Acceptor, Handler]); ok && acceptor(handler.Acceptor()) {
|
||
if err := handelr(handler.Handler()); err != nil {
|
||
return true, err
|
||
}
|
||
return true, nil
|
||
}
|
||
}
|
||
|
||
return false, nil
|
||
}
|
||
|
||
func invokeAllHookHandler[Acceptor, Handler any](
|
||
hook api.HookType[Acceptor, Handler],
|
||
acceptor func(Acceptor) bool,
|
||
handelr func(Handler) error,
|
||
) error {
|
||
pluginLock.RLock()
|
||
defer pluginLock.RUnlock()
|
||
|
||
for _, handlers := range hooks {
|
||
if handler, ok := handlers[hook.Key()].(api.HookHandler[Acceptor, Handler]); ok && acceptor(handler.Acceptor()) {
|
||
if err := handelr(handler.Handler()); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|