JCS-pub/common/pkgs/sysevent/watcher.go

149 lines
2.9 KiB
Go

package sysevent
import (
"fmt"
"sync"
"github.com/streadway/amqp"
"gitlink.org.cn/cloudream/common/pkgs/async"
"gitlink.org.cn/cloudream/common/utils/lo2"
"gitlink.org.cn/cloudream/common/utils/serder"
)
type Watcher interface {
OnEvent(event SysEvent)
}
type WatcherEvent interface{}
type WatcherExited struct {
Err error
}
type WatcherHost struct {
watchers []Watcher
lock sync.Mutex
connection *amqp.Connection
channel *amqp.Channel
recvChan <-chan amqp.Delivery
}
func NewWatcherHost(cfg Config) (*WatcherHost, error) {
config := amqp.Config{
Vhost: cfg.VHost,
}
url := fmt.Sprintf("amqp://%s:%s@%s", cfg.Account, cfg.Password, cfg.Address)
connection, err := amqp.DialConfig(url, config)
if err != nil {
return nil, err
}
channel, err := connection.Channel()
if err != nil {
connection.Close()
return nil, fmt.Errorf("openning channel on connection: %w", err)
}
err = channel.ExchangeDeclare(cfg.Exchange, "fanout", false, true, false, false, nil)
if err != nil {
connection.Close()
return nil, fmt.Errorf("declare exchange: %w", err)
}
_, err = channel.QueueDeclare(
cfg.Queue,
false,
true,
false,
false,
nil,
)
if err != nil {
channel.Close()
connection.Close()
return nil, fmt.Errorf("declare queue: %w", err)
}
err = channel.QueueBind(cfg.Queue, "", cfg.Exchange, false, nil)
if err != nil {
channel.Close()
connection.Close()
return nil, fmt.Errorf("bind queue: %w", err)
}
recvChan, err := channel.Consume(cfg.Queue, "", true, false, true, false, nil)
if err != nil {
channel.Close()
connection.Close()
return nil, fmt.Errorf("consume queue: %w", err)
}
wat := &WatcherHost{
connection: connection,
channel: channel,
recvChan: recvChan,
}
return wat, nil
}
func (w *WatcherHost) Start() *async.UnboundChannel[WatcherEvent] {
ch := async.NewUnboundChannel[WatcherEvent]()
go func() {
defer ch.Close()
defer w.channel.Close()
defer w.connection.Close()
for m := range w.recvChan {
evt, err := serder.JSONToObjectEx[SysEvent](m.Body)
if err != nil {
ch.Send(OtherError{Err: fmt.Errorf("deserialize event: %w", err)})
continue
}
w.lock.Lock()
ws := make([]Watcher, 0, len(w.watchers))
ws = append(ws, w.watchers...)
w.lock.Unlock()
for _, w := range ws {
w.OnEvent(evt)
}
}
ch.Send(WatcherExited{Err: nil})
}()
return ch
}
func (w *WatcherHost) AddWatcher(watcher Watcher) {
w.lock.Lock()
defer w.lock.Unlock()
w.watchers = append(w.watchers, watcher)
}
func (w *WatcherHost) AddWatcherFn(fn func(event SysEvent)) Watcher {
watcher := &fnWatcher{fn: fn}
w.AddWatcher(watcher)
return watcher
}
func (w *WatcherHost) RemoveWatcher(watcher Watcher) {
w.lock.Lock()
defer w.lock.Unlock()
w.watchers = lo2.Remove(w.watchers, watcher)
}
type fnWatcher struct {
fn func(event SysEvent)
}
func (w *fnWatcher) OnEvent(event SysEvent) {
w.fn(event)
}