优化结构
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user