Browse Source

Merge branch 'feature_gxh'

gitlink
Sydonian 10 months ago
parent
commit
9ef202efd5
4 changed files with 257 additions and 0 deletions
  1. +8
    -0
      common/pkgs/sysevent/config.go
  2. +119
    -0
      common/pkgs/sysevent/publisher.go
  3. +9
    -0
      common/pkgs/sysevent/sysevent.go
  4. +121
    -0
      common/pkgs/sysevent/watcher.go

+ 8
- 0
common/pkgs/sysevent/config.go View File

@@ -0,0 +1,8 @@
package sysevent

type Config struct {
Address string `json:"address"`
Account string `json:"account"`
Password string `json:"password"`
VHost string `json:"vhost"`
}

+ 119
- 0
common/pkgs/sysevent/publisher.go View File

@@ -0,0 +1,119 @@
package sysevent

import (
"fmt"

"github.com/streadway/amqp"
"gitlink.org.cn/cloudream/common/pkgs/async"
"gitlink.org.cn/cloudream/common/utils/serder"
)

type PublisherEvent interface{}

type PublisherExited struct {
Err error
}

type PublishError struct {
Err error
}

type OtherError struct {
Err error
}

type Publisher struct {
connection *amqp.Connection
channel *amqp.Channel
eventChan *async.UnboundChannel[SysEvent]
thisSource Source
}

func NewPublisher(cfg Config, thisSource Source) (*Publisher, 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.QueueDeclare(
SysEventQueueName,
false,
true,
false,
false,
nil,
)
if err != nil {
return nil, fmt.Errorf("declare queue: %w", err)
}

pub := &Publisher{
connection: connection,
channel: channel,
eventChan: async.NewUnboundChannel[SysEvent](),
thisSource: thisSource,
}

return pub, nil
}

func (p *Publisher) Start() *async.UnboundChannel[PublisherEvent] {
ch := async.NewUnboundChannel[PublisherEvent]()
go func() {
defer ch.Close()
defer p.channel.Close()
defer p.connection.Close()

for {
event := <-p.eventChan.Receive().Chan()
if event.Err != nil {
if event.Err == async.ErrChannelClosed {
ch.Send(PublisherExited{Err: nil})
} else {
ch.Send(PublisherExited{Err: event.Err})
}
return
}

eventData, err := serder.ObjectToJSONEx(event.Value)
if err != nil {
ch.Send(OtherError{Err: fmt.Errorf("serialize event data: %w", err)})
continue
}

err = p.channel.Publish("", SysEventQueueName, false, false, amqp.Publishing{
ContentType: "text/plain",
Body: eventData,
Expiration: "60000", // 消息超时时间默认1分钟
})
if err != nil {
ch.Send(PublishError{Err: err})
continue
}
}
}()

return ch
}

// Publish 发布事件,自动补齐时间戳和源信息
func (p *Publisher) Publish(evt SysEvent) {
// TODO 补齐时间戳和源信息
p.eventChan.Send(evt)
}

// PublishRaw 完全原样发布事件,不补齐任何信息
func (p *Publisher) PublishRaw(evt SysEvent) {
p.eventChan.Send(evt)
}

+ 9
- 0
common/pkgs/sysevent/sysevent.go View File

@@ -0,0 +1,9 @@
package sysevent

const (
SysEventQueueName = "SysEventQueue"
)

type SysEvent = any // TODO 换成具体的类型

type Source = any // TODO 换成具体的类型

+ 121
- 0
common/pkgs/sysevent/watcher.go View File

@@ -0,0 +1,121 @@
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.QueueDeclare(
SysEventQueueName,
false,
true,
false,
false,
nil,
)
if err != nil {
channel.Close()
connection.Close()
return nil, fmt.Errorf("declare queue: %w", err)
}

recvChan, err := channel.Consume(SysEventQueueName, "", 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) RemoveWatcher(watcher Watcher) {
w.lock.Lock()
defer w.lock.Unlock()

w.watchers = lo2.Remove(w.watchers, watcher)
}

Loading…
Cancel
Save