fox/rmq/producer.go
2025-08-27 23:02:26 +08:00

81 lines
1.6 KiB
Go

package rmq
import (
"context"
"github.com/fox/fox/log"
"github.com/pkg/errors"
amqp "github.com/rabbitmq/amqp091-go"
"time"
)
type Producer struct {
connection *Connection
config *RabbitMQConfig
}
func NewProducer(conn *Connection, config *RabbitMQConfig) *Producer {
return &Producer{
connection: conn,
config: config,
}
}
func (p *Producer) Publish(ctx context.Context, message string) error {
if !p.connection.IsConnected() {
return errors.New("not connected to RabbitMQ")
}
// 重试机制
for attempt := 1; attempt <= p.config.MaxRetries; attempt++ {
err := p.publishWithRetry(ctx, []byte(message))
if err == nil {
return nil
}
log.WarnF("Publish attempt %d failed: %v", attempt, err)
if attempt < p.config.MaxRetries {
time.Sleep(time.Duration(attempt) * time.Second)
}
}
return errors.New("failed to publish message after all retries")
}
func (p *Producer) publishWithRetry(ctx context.Context, body []byte) error {
channel := p.connection.GetChannel()
err := channel.PublishWithContext(
ctx,
p.config.ExchangeName,
p.config.RoutingKey,
false,
false,
amqp.Publishing{
ContentType: "application/json",
Body: body,
DeliveryMode: amqp.Persistent,
Timestamp: time.Now(),
},
)
if err != nil {
return errors.Wrap(err, "failed to publish message")
}
// 等待确认
select {
case confirm := <-p.connection.notifyConfirm:
if !confirm.Ack {
return errors.New("message not acknowledged by broker")
}
case <-time.After(5 * time.Second):
return errors.New("message confirmation timeout")
}
return nil
}
func (p *Producer) Close() error {
return p.connection.Close()
}