优化结构

This commit is contained in:
Yvan
2026-08-21 23:29:28 +08:00
parent 5d5784f535
commit 626f505b5d
3 changed files with 180 additions and 38 deletions
+88 -38
View File
@@ -15,8 +15,9 @@ import (
)
const (
ProviderEMQX = "emqx"
ProviderRabbitMQ = "rabbitmq"
ProviderEMQX = platformmq.ProviderEMQX
ProviderRabbitMQ = platformmq.ProviderRabbitMQ
retryTick = time.Second
)
// Reloadable owns the process-wide message clients. Configuration comes only
@@ -25,8 +26,15 @@ type Reloadable struct {
mu sync.RWMutex
opMu sync.Mutex
clients map[string]platformmq.Client
subscriptions map[string]map[string]subscription
configs map[string]runtimeconfig.Config
subscriptions map[string]map[string]map[string]subscription
bindings map[string]map[string]byte
pending map[string]bool
nextRetry map[string]time.Time
stop []func()
retryStop chan struct{}
retryDone chan struct{}
closeOnce sync.Once
logger *slog.Logger
closed bool
}
@@ -47,7 +55,13 @@ func New(store *runtimeconfig.Store, logger *slog.Logger) (*Reloadable, func(),
}
r := &Reloadable{
clients: make(map[string]platformmq.Client),
subscriptions: make(map[string]map[string]subscription),
configs: make(map[string]runtimeconfig.Config),
subscriptions: make(map[string]map[string]map[string]subscription),
bindings: make(map[string]map[string]byte),
pending: make(map[string]bool),
nextRetry: make(map[string]time.Time),
retryStop: make(chan struct{}),
retryDone: make(chan struct{}),
logger: logger,
}
if store != nil {
@@ -58,6 +72,7 @@ func New(store *runtimeconfig.Store, logger *slog.Logger) (*Reloadable, func(),
store.Subscribe("mq", ProviderRabbitMQ, func(config runtimeconfig.Config) { r.apply(ProviderRabbitMQ, config) }),
)
}
go r.retryLoop()
cleanup := func() {
for _, stop := range r.stop {
stop()
@@ -118,26 +133,45 @@ func TestConfig(ctx context.Context, provider string, raw json.RawMessage) error
}
func (r *Reloadable) apply(provider string, config runtimeconfig.Config) {
provider = strings.ToLower(strings.TrimSpace(provider))
r.opMu.Lock()
defer r.opMu.Unlock()
if r.closed {
return
}
config.Provider = provider
config.Values = append(json.RawMessage(nil), config.Values...)
r.configs[provider] = config
if !config.Enabled {
delete(r.pending, provider)
delete(r.nextRetry, provider)
r.replaceClientLocked(provider, nil)
return
}
if err := r.activateLocked(provider, config); err != nil {
r.pending[provider] = true
r.nextRetry[provider] = time.Now().Add(configRetryInterval(config.Values))
r.logger.Warn("message integration unavailable", "mod", "mq", "provider", provider, "error", err)
}
}
func (r *Reloadable) activateLocked(provider string, config runtimeconfig.Config) error {
client, err := newProviderClient(provider, config.Values)
if err != nil {
r.logger.Warn("message integration unavailable", "mod", "mq", "provider", provider, "error", err)
return
return err
}
if err = r.restoreSubscriptionsLocked(provider, client); err != nil {
bindings, err := r.restoreSubscriptionsLocked(provider, client)
if err != nil {
_ = client.Close()
r.logger.Warn("restore message subscriptions failed", "mod", "mq", "provider", provider, "error", err)
return
return fmt.Errorf("restore message subscriptions: %w", err)
}
r.replaceClientLocked(provider, client)
r.mu.Lock()
r.bindings[provider] = bindings
r.mu.Unlock()
delete(r.pending, provider)
delete(r.nextRetry, provider)
return nil
}
func newProviderClient(provider string, raw json.RawMessage) (platformmq.Client, error) {
@@ -148,33 +182,35 @@ func newProviderClient(provider string, raw json.RawMessage) (platformmq.Client,
switch provider {
case ProviderEMQX:
return platformmq.NewMQTT(platformmq.Config{
Enabled: true,
Broker: configText(values, "broker"),
ClientID: configText(values, "client_id"),
Username: configText(values, "username"),
Password: configText(values, "password"),
KeepAlive: configSeconds(values, "keep_alive"),
CleanSession: configBool(values, "clean_session"),
ConnectTimeout: configSeconds(values, "connect_timeout"),
Enabled: true,
Broker: configText(values, "broker"),
ClientID: configText(values, "client_id"),
Username: configText(values, "username"),
Password: configText(values, "password"),
KeepAlive: configSeconds(values, "keep_alive"),
CleanSession: configBool(values, "clean_session"),
ConnectTimeout: configSeconds(values, "connect_timeout"),
ReconnectInterval: configSeconds(values, "reconnect_interval"),
})
case ProviderRabbitMQ:
return platformmq.NewRabbitMQ(platformmq.RabbitMQConfig{
Enabled: true,
Host: configText(values, "host"),
Port: configInt(values, "port"),
Username: configText(values, "username"),
Password: configText(values, "password"),
VHost: configText(values, "vhost"),
Exchange: configText(values, "exchange"),
ExchangeType: configText(values, "exchange_type"),
Queue: configText(values, "queue"),
RoutingKey: configText(values, "routing_key"),
Durable: configBool(values, "durable"),
AutoDelete: configBool(values, "auto_delete"),
PrefetchCount: configInt(values, "prefetch_count"),
Heartbeat: configSeconds(values, "heartbeat"),
ConnectTimeout: configSeconds(values, "connect_timeout"),
TLS: configBool(values, "tls"),
Enabled: true,
Host: configText(values, "host"),
Port: configInt(values, "port"),
Username: configText(values, "username"),
Password: configText(values, "password"),
VHost: configText(values, "vhost"),
Exchange: configText(values, "exchange"),
ExchangeType: configText(values, "exchange_type"),
Queue: configText(values, "queue"),
RoutingKey: configText(values, "routing_key"),
Durable: configBool(values, "durable"),
AutoDelete: configBool(values, "auto_delete"),
PrefetchCount: configInt(values, "prefetch_count"),
Heartbeat: configSeconds(values, "heartbeat"),
ConnectTimeout: configSeconds(values, "connect_timeout"),
ReconnectInterval: configSeconds(values, "reconnect_interval"),
TLS: configBool(values, "tls"),
})
default:
return nil, fmt.Errorf("unsupported message provider %q", provider)
@@ -222,6 +258,7 @@ func (r *Reloadable) replaceClientLocked(provider string, next platformmq.Client
old := r.clients[provider]
if next == nil {
delete(r.clients, provider)
delete(r.bindings, provider)
} else {
r.clients[provider] = next
}
@@ -231,13 +268,26 @@ func (r *Reloadable) replaceClientLocked(provider string, next platformmq.Client
}
}
func (r *Reloadable) restoreSubscriptionsLocked(provider string, client platformmq.Client) error {
for topic, item := range r.subscriptions[provider] {
if err := client.Subscribe(context.Background(), topic, item.qos, item.handler); err != nil {
return err
func (r *Reloadable) restoreSubscriptionsLocked(provider string, client platformmq.Client) (map[string]byte, error) {
desired := r.desiredSubscriptions(provider)
bindings := make(map[string]byte, len(desired))
for topic, qos := range desired {
if err := client.Subscribe(context.Background(), topic, qos, r.dispatcher(provider, topic)); err != nil {
return nil, err
}
bindings[topic] = qos
}
return nil
return bindings, nil
}
func configRetryInterval(raw json.RawMessage) time.Duration {
values := map[string]any{}
_ = json.Unmarshal(raw, &values)
interval := configSeconds(values, "reconnect_interval")
if interval <= 0 {
return 5 * time.Second
}
return interval
}
func (r *Reloadable) client(provider string) platformmq.Client {