2026-02-03 17:05:32 +08:00
|
|
|
|
// Package mq provides read or write access to message queue services
|
|
|
|
|
|
package mq
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"context"
|
2026-03-02 17:00:09 +08:00
|
|
|
|
"encoding/json"
|
2026-02-03 17:05:32 +08:00
|
|
|
|
"time"
|
|
|
|
|
|
|
2026-02-28 17:38:33 +08:00
|
|
|
|
"modelRT/constants"
|
2026-02-03 17:05:32 +08:00
|
|
|
|
"modelRT/logger"
|
2026-03-02 17:00:09 +08:00
|
|
|
|
"modelRT/mq/event"
|
2026-02-03 17:05:32 +08:00
|
|
|
|
|
|
|
|
|
|
amqp "github.com/rabbitmq/amqp091-go"
|
2026-05-07 16:43:34 +08:00
|
|
|
|
"go.opentelemetry.io/otel"
|
|
|
|
|
|
"go.opentelemetry.io/otel/propagation"
|
2026-02-03 17:05:32 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
2026-05-07 16:43:34 +08:00
|
|
|
|
// amqpHeaderCarrier adapts amqp.Table to propagation.TextMapCarrier for trace context injection.
|
|
|
|
|
|
type amqpHeaderCarrier amqp.Table
|
|
|
|
|
|
|
|
|
|
|
|
func (c amqpHeaderCarrier) Get(key string) string {
|
|
|
|
|
|
val, ok := amqp.Table(c)[key]
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
return ""
|
|
|
|
|
|
}
|
|
|
|
|
|
str, _ := val.(string)
|
|
|
|
|
|
return str
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (c amqpHeaderCarrier) Set(key, value string) {
|
|
|
|
|
|
amqp.Table(c)[key] = value
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (c amqpHeaderCarrier) Keys() []string {
|
|
|
|
|
|
keys := make([]string, 0, len(c))
|
|
|
|
|
|
for k := range c {
|
|
|
|
|
|
keys = append(keys, k)
|
|
|
|
|
|
}
|
|
|
|
|
|
return keys
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var _ propagation.TextMapCarrier = amqpHeaderCarrier{}
|
|
|
|
|
|
|
2026-05-11 17:34:27 +08:00
|
|
|
|
// EventMessage wraps an EventRecord with the trace context of the computation cycle that produced it.
|
|
|
|
|
|
type EventMessage struct {
|
|
|
|
|
|
Record *event.EventRecord
|
|
|
|
|
|
TraceCarrier map[string]string
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-25 17:14:25 +08:00
|
|
|
|
// MsgChan define variable of channel to store messages that need to be sent to rabbitMQ
|
2026-05-11 17:34:27 +08:00
|
|
|
|
var MsgChan chan *EventMessage
|
2026-02-25 17:14:25 +08:00
|
|
|
|
|
|
|
|
|
|
func init() {
|
2026-05-11 17:34:27 +08:00
|
|
|
|
MsgChan = make(chan *EventMessage, 10000)
|
2026-02-25 17:14:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 17:38:33 +08:00
|
|
|
|
func initUpDownLimitEventChannel(ctx context.Context) (*amqp.Channel, error) {
|
2026-02-03 17:05:32 +08:00
|
|
|
|
var channel *amqp.Channel
|
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
|
|
|
|
channel, err = GetConn().Channel()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
logger.Error(ctx, "open rabbitMQ server channel failed", "error", err)
|
2026-03-02 17:00:09 +08:00
|
|
|
|
return nil, err
|
2026-02-03 17:05:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 17:38:33 +08:00
|
|
|
|
err = channel.ExchangeDeclare(constants.EventDeadExchangeName, "topic", true, false, false, false, nil)
|
2026-02-03 17:05:32 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
logger.Error(ctx, "declare event dead letter exchange failed", "error", err)
|
2026-03-02 17:00:09 +08:00
|
|
|
|
return nil, err
|
2026-02-03 17:05:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 17:38:33 +08:00
|
|
|
|
_, err = channel.QueueDeclare(constants.EventUpDownDeadQueueName, true, false, false, false, nil)
|
2026-02-03 17:05:32 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
logger.Error(ctx, "declare event dead letter queue failed", "error", err)
|
2026-03-02 17:00:09 +08:00
|
|
|
|
return nil, err
|
2026-02-03 17:05:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-02 17:00:09 +08:00
|
|
|
|
err = channel.QueueBind(constants.EventUpDownDeadQueueName, "#", constants.EventDeadExchangeName, false, nil)
|
2026-02-03 17:05:32 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
logger.Error(ctx, "bind event dead letter queue with routing key and exchange failed", "error", err)
|
2026-03-02 17:00:09 +08:00
|
|
|
|
return nil, err
|
2026-02-03 17:05:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 17:38:33 +08:00
|
|
|
|
err = channel.ExchangeDeclare(constants.EventExchangeName, "topic", true, false, false, false, nil)
|
2026-02-03 17:05:32 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
logger.Error(ctx, "declare event exchange failed", "error", err)
|
2026-03-02 17:00:09 +08:00
|
|
|
|
return nil, err
|
2026-02-03 17:05:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
args := amqp.Table{
|
|
|
|
|
|
"x-max-length": int32(50),
|
2026-02-28 17:38:33 +08:00
|
|
|
|
"x-dead-letter-exchange": constants.EventDeadExchangeName,
|
|
|
|
|
|
"x-dead-letter-routing-key": constants.EventUpDownDeadRoutingKey,
|
2026-02-03 17:05:32 +08:00
|
|
|
|
}
|
2026-02-28 17:38:33 +08:00
|
|
|
|
_, err = channel.QueueDeclare(constants.EventUpDownQueueName, true, false, false, false, args)
|
2026-02-03 17:05:32 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
logger.Error(ctx, "declare event queue failed", "error", err)
|
2026-03-02 17:00:09 +08:00
|
|
|
|
return nil, err
|
2026-02-03 17:05:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 17:38:33 +08:00
|
|
|
|
err = channel.QueueBind(constants.EventUpDownQueueName, constants.EventUpDownRoutingKey, constants.EventExchangeName, false, nil)
|
2026-02-03 17:05:32 +08:00
|
|
|
|
if err != nil {
|
2026-03-02 17:00:09 +08:00
|
|
|
|
logger.Error(ctx, "bind event queue with routing key and exchange failed", "error", err)
|
|
|
|
|
|
return nil, err
|
2026-02-03 17:05:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := channel.Confirm(false); err != nil {
|
|
|
|
|
|
logger.Error(ctx, "channel could not be put into confirm mode", "error", err)
|
2026-03-02 17:00:09 +08:00
|
|
|
|
return nil, err
|
2026-02-03 17:05:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
return channel, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 17:38:33 +08:00
|
|
|
|
// PushUpDownLimitEventToRabbitMQ define func to push up and down limit event message to rabbitMQ
|
2026-05-11 17:34:27 +08:00
|
|
|
|
func PushUpDownLimitEventToRabbitMQ(ctx context.Context, msgChan chan *EventMessage) {
|
2026-02-28 17:38:33 +08:00
|
|
|
|
channel, err := initUpDownLimitEventChannel(ctx)
|
2026-02-03 17:05:32 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
logger.Error(ctx, "initializing rabbitMQ channel failed", "error", err)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
// TODO 使用配置修改确认模式通道参数
|
|
|
|
|
|
confirms := channel.NotifyPublish(make(chan amqp.Confirmation, 100))
|
|
|
|
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
|
|
for {
|
|
|
|
|
|
select {
|
|
|
|
|
|
case confirm, ok := <-confirms:
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if !confirm.Ack {
|
|
|
|
|
|
logger.Error(ctx, "publish message failed (rejected by rabbitMQ)", "tag", confirm.DeliveryTag)
|
|
|
|
|
|
}
|
|
|
|
|
|
case <-ctx.Done():
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
for {
|
|
|
|
|
|
select {
|
|
|
|
|
|
case <-ctx.Done():
|
|
|
|
|
|
logger.Info(ctx, "push event alarm message to rabbitMQ stopped by context cancel")
|
|
|
|
|
|
channel.Close()
|
|
|
|
|
|
return
|
2026-05-11 17:34:27 +08:00
|
|
|
|
case msg, ok := <-msgChan:
|
2026-02-03 17:05:32 +08:00
|
|
|
|
if !ok {
|
|
|
|
|
|
logger.Info(ctx, "push event alarm message to rabbitMQ stopped by msgChan closed, exiting push loop")
|
|
|
|
|
|
channel.Close()
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-11 17:34:27 +08:00
|
|
|
|
eventRecord := msg.Record
|
|
|
|
|
|
|
2026-03-02 17:00:09 +08:00
|
|
|
|
// TODO 将消息的序列化移动到发送之前,以便使用eventRecord的category来作为routing key
|
|
|
|
|
|
recordBytes, err := json.Marshal(eventRecord)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
logger.Error(ctx, "marshal event record failed", "event_uuid", eventRecord.EventUUID, "error", err)
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-11 17:34:27 +08:00
|
|
|
|
// restore computation cycle trace context so the AMQP message carries the correct parent span
|
|
|
|
|
|
msgCtx := otel.GetTextMapPropagator().Extract(ctx, propagation.MapCarrier(msg.TraceCarrier))
|
2026-05-07 16:43:34 +08:00
|
|
|
|
headers := amqp.Table{}
|
2026-05-11 17:34:27 +08:00
|
|
|
|
otel.GetTextMapPropagator().Inject(msgCtx, amqpHeaderCarrier(headers))
|
2026-05-07 16:43:34 +08:00
|
|
|
|
|
2026-02-03 17:05:32 +08:00
|
|
|
|
// send event alarm message to rabbitMQ queue
|
2026-03-02 17:00:09 +08:00
|
|
|
|
routingKey := eventRecord.Category
|
2026-02-03 17:05:32 +08:00
|
|
|
|
pubCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
|
|
|
|
|
err = channel.PublishWithContext(pubCtx,
|
2026-03-02 17:00:09 +08:00
|
|
|
|
constants.EventExchangeName, // exchange
|
|
|
|
|
|
routingKey, // routing key
|
|
|
|
|
|
false, // mandatory
|
|
|
|
|
|
false, // immediate
|
2026-02-03 17:05:32 +08:00
|
|
|
|
amqp.Publishing{
|
|
|
|
|
|
ContentType: "text/plain",
|
2026-03-02 17:00:09 +08:00
|
|
|
|
Body: recordBytes,
|
2026-05-07 16:43:34 +08:00
|
|
|
|
Headers: headers,
|
2026-02-03 17:05:32 +08:00
|
|
|
|
})
|
|
|
|
|
|
cancel()
|
|
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
2026-03-02 17:00:09 +08:00
|
|
|
|
logger.Error(ctx, "publish message to rabbitMQ queue failed", "message", recordBytes, "error", err)
|
2026-02-03 17:05:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|