notification_gocdk_pub_sub.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. //go:build gocdk
  2. // +build gocdk
  3. package sub
  4. import (
  5. "context"
  6. amqp "github.com/rabbitmq/amqp091-go"
  7. "github.com/seaweedfs/seaweedfs/weed/glog"
  8. "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
  9. "github.com/seaweedfs/seaweedfs/weed/util"
  10. "gocloud.dev/pubsub"
  11. _ "gocloud.dev/pubsub/awssnssqs"
  12. "gocloud.dev/pubsub/rabbitpubsub"
  13. "google.golang.org/protobuf/proto"
  14. "net/url"
  15. "os"
  16. "path"
  17. "strings"
  18. "time"
  19. // _ "gocloud.dev/pubsub/azuresb"
  20. _ "gocloud.dev/pubsub/gcppubsub"
  21. _ "gocloud.dev/pubsub/natspubsub"
  22. _ "gocloud.dev/pubsub/rabbitpubsub"
  23. )
  24. func init() {
  25. NotificationInputs = append(NotificationInputs, &GoCDKPubSubInput{})
  26. }
  27. func getPath(rawUrl string) string {
  28. parsedUrl, _ := url.Parse(rawUrl)
  29. return path.Join(parsedUrl.Host, parsedUrl.Path)
  30. }
  31. func QueueDeclareAndBind(conn *amqp.Connection, exchangeUrl string, queueUrl string) error {
  32. exchangeName := getPath(exchangeUrl)
  33. queueName := getPath(queueUrl)
  34. exchangeNameDLX := "DLX." + exchangeName
  35. queueNameDLX := "DLX." + queueName
  36. ch, err := conn.Channel()
  37. if err != nil {
  38. glog.Error(err)
  39. return err
  40. }
  41. defer ch.Close()
  42. if err := ch.ExchangeDeclare(
  43. exchangeNameDLX, "fanout", true, false, false, false, nil); err != nil {
  44. glog.Error(err)
  45. return err
  46. }
  47. if err := ch.ExchangeDeclare(
  48. exchangeName, "fanout", true, false, false, false, nil); err != nil {
  49. glog.Error(err)
  50. return err
  51. }
  52. if _, err := ch.QueueDeclare(
  53. queueName, true, false, false, false,
  54. amqp.Table{"x-dead-letter-exchange": exchangeNameDLX}); err != nil {
  55. glog.Error(err)
  56. return err
  57. }
  58. if err := ch.QueueBind(queueName, "", exchangeName, false, nil); err != nil {
  59. glog.Error(err)
  60. return err
  61. }
  62. if _, err := ch.QueueDeclare(
  63. queueNameDLX, true, false, false, false,
  64. amqp.Table{"x-dead-letter-exchange": exchangeName, "x-message-ttl": 600000}); err != nil {
  65. glog.Error(err)
  66. return err
  67. }
  68. if err := ch.QueueBind(queueNameDLX, "", exchangeNameDLX, false, nil); err != nil {
  69. glog.Error(err)
  70. return err
  71. }
  72. return nil
  73. }
  74. type GoCDKPubSubInput struct {
  75. sub *pubsub.Subscription
  76. subURL string
  77. }
  78. func (k *GoCDKPubSubInput) GetName() string {
  79. return "gocdk_pub_sub"
  80. }
  81. func (k *GoCDKPubSubInput) Initialize(configuration util.Configuration, prefix string) error {
  82. topicUrl := configuration.GetString(prefix + "topic_url")
  83. k.subURL = configuration.GetString(prefix + "sub_url")
  84. glog.V(0).Infof("notification.gocdk_pub_sub.sub_url: %v", k.subURL)
  85. sub, err := pubsub.OpenSubscription(context.Background(), k.subURL)
  86. if err != nil {
  87. return err
  88. }
  89. var conn *amqp.Connection
  90. if sub.As(&conn) {
  91. ch, err := conn.Channel()
  92. if err != nil {
  93. return err
  94. }
  95. defer ch.Close()
  96. _, err = ch.QueueInspect(getPath(k.subURL))
  97. if err != nil {
  98. if strings.HasPrefix(err.Error(), "Exception (404) Reason") {
  99. if err := QueueDeclareAndBind(conn, topicUrl, k.subURL); err != nil {
  100. return err
  101. }
  102. } else {
  103. return err
  104. }
  105. }
  106. }
  107. k.sub = sub
  108. return nil
  109. }
  110. func (k *GoCDKPubSubInput) ReceiveMessage() (key string, message *filer_pb.EventNotification, onSuccessFn func(), onFailureFn func(), err error) {
  111. ctx := context.Background()
  112. msg, err := k.sub.Receive(ctx)
  113. if err != nil {
  114. var conn *amqp.Connection
  115. if k.sub.As(&conn) && conn.IsClosed() {
  116. conn.Close()
  117. k.sub.Shutdown(ctx)
  118. conn, err = amqp.Dial(os.Getenv("RABBIT_SERVER_URL"))
  119. if err != nil {
  120. glog.Error(err)
  121. time.Sleep(time.Second)
  122. return
  123. }
  124. k.sub = rabbitpubsub.OpenSubscription(conn, getPath(k.subURL), nil)
  125. return
  126. }
  127. // This is permanent cached sub err
  128. glog.Fatal(err)
  129. }
  130. onFailureFn = func() {
  131. if msg.Nackable() {
  132. isRedelivered := false
  133. var delivery amqp.Delivery
  134. if msg.As(&delivery) {
  135. isRedelivered = delivery.Redelivered
  136. glog.Warningf("onFailureFn() metadata: %+v, redelivered: %v", msg.Metadata, delivery.Redelivered)
  137. }
  138. if isRedelivered {
  139. if err := delivery.Nack(false, false); err != nil {
  140. glog.Error(err)
  141. }
  142. } else {
  143. msg.Nack()
  144. }
  145. }
  146. }
  147. onSuccessFn = func() {
  148. msg.Ack()
  149. }
  150. key = msg.Metadata["key"]
  151. message = &filer_pb.EventNotification{}
  152. err = proto.Unmarshal(msg.Body, message)
  153. if err != nil {
  154. return "", nil, onSuccessFn, onFailureFn, err
  155. }
  156. return key, message, onSuccessFn, onFailureFn, nil
  157. }