Files
mc-gateway/cmd/gateway/plugin.go
tursom f508ecc1b9
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
docs: 补充中文代码注释
2026-06-27 20:15:29 +08:00

256 lines
6.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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
}