1
- package com .avast .clients .rabbitmq
1
+ package com .avast .clients .rabbitmq . publisher
2
2
3
- import cats .effect .concurrent .{Deferred , Ref }
4
3
import cats .effect .{Blocker , ConcurrentEffect , ContextShift , Sync }
5
4
import cats .syntax .applicativeError ._
6
5
import cats .syntax .flatMap ._
7
- import cats .syntax .foldable ._
8
6
import cats .syntax .functor ._
9
7
import com .avast .bytes .Bytes
10
- import com .avast .clients .rabbitmq .DefaultRabbitMQProducer .SentMessages
11
- import com .avast .clients .rabbitmq .JavaConverters ._
12
8
import com .avast .clients .rabbitmq .api .CorrelationIdStrategy .FromPropertiesOrRandomNew
13
9
import com .avast .clients .rabbitmq .api ._
10
+ import com .avast .clients .rabbitmq .JavaConverters ._
14
11
import com .avast .clients .rabbitmq .logging .ImplicitContextLogger
12
+ import com .avast .clients .rabbitmq .{startAndForget , CorrelationId , ProductConverter , ServerChannel }
15
13
import com .avast .metrics .scalaeffectapi .Monitor
16
14
import com .rabbitmq .client .AMQP .BasicProperties
17
- import com .rabbitmq .client .{AlreadyClosedException , ConfirmListener , ReturnListener }
15
+ import com .rabbitmq .client .{AlreadyClosedException , ReturnListener }
18
16
19
17
import java .util .UUID
20
18
import scala .util .control .NonFatal
21
19
22
- class DefaultRabbitMQProducer [F [_], A : ProductConverter ](name : String ,
23
- exchangeName : String ,
24
- channel : ServerChannel ,
25
- defaultProperties : MessageProperties ,
26
- sentMessages : Option [SentMessages [F ]],
27
- publisherConfirmsConfig : Option [PublisherConfirmsConfig ],
28
- reportUnroutable : Boolean ,
29
- sizeLimitBytes : Option [Int ],
30
- blocker : Blocker ,
31
- logger : ImplicitContextLogger [F ],
32
- monitor : Monitor [F ])(implicit F : ConcurrentEffect [F ], cs : ContextShift [F ])
20
+ abstract class BaseRabbitMQProducer [F [_], A : ProductConverter ](name : String ,
21
+ exchangeName : String ,
22
+ channel : ServerChannel ,
23
+ defaultProperties : MessageProperties ,
24
+ reportUnroutable : Boolean ,
25
+ sizeLimitBytes : Option [Int ],
26
+ blocker : Blocker ,
27
+ logger : ImplicitContextLogger [F ],
28
+ monitor : Monitor [F ])(implicit F : ConcurrentEffect [F ], cs : ContextShift [F ])
33
29
extends RabbitMQProducer [F , A ] {
34
30
35
31
private val sentMeter = monitor.meter(" sent" )
36
32
private val sentFailedMeter = monitor.meter(" sentFailed" )
37
33
private val unroutableMeter = monitor.meter(" unroutable" )
38
- private val acked = monitor.meter(" acked" )
39
- private val nacked = monitor.meter(" nacked" )
40
34
41
35
private val converter = implicitly[ProductConverter [A ]]
42
36
43
37
private val sendLock = new Object
44
38
45
39
channel.addReturnListener(if (reportUnroutable) LoggingReturnListener else NoOpReturnListener )
46
- publisherConfirmsConfig.foreach(cfg => if (cfg.enabled) {
47
- channel.confirmSelect()
48
- channel.addConfirmListener(DefaultConfirmListener )
49
- })
40
+
41
+ def sendMessage (routingKey : String , body : Bytes , properties : MessageProperties )(implicit correlationId : CorrelationId ): F [Unit ]
50
42
51
43
override def send (routingKey : String , body : A , properties : Option [MessageProperties ] = None )(
52
44
implicit cidStrategy : CorrelationIdStrategy = FromPropertiesOrRandomNew (properties)): F [Unit ] = {
@@ -65,17 +57,13 @@ class DefaultRabbitMQProducer[F[_], A: ProductConverter](name: String,
65
57
case Right (convertedBody) =>
66
58
for {
67
59
_ <- checkSize(convertedBody, routingKey)
68
- result = publisherConfirmsConfig match {
69
- case Some (cfg @ PublisherConfirmsConfig (true , _)) => sendWithAck(routingKey, convertedBody, finalProperties, 1 , cfg)
70
- case _ => send(routingKey, convertedBody, finalProperties)
71
- }
72
- _ <- logErrors(result, routingKey)
60
+ _ <- logErrors(sendMessage(routingKey, convertedBody, finalProperties), routingKey)
73
61
} yield ()
74
62
case Left (ce) => Sync [F ].raiseError(ce)
75
63
}
76
64
}
77
65
78
- private def send (routingKey : String , body : Bytes , properties : MessageProperties )(implicit correlationId : CorrelationId ): F [Unit ] = {
66
+ protected def basicSend (routingKey : String , body : Bytes , properties : MessageProperties )(implicit correlationId : CorrelationId ): F [Unit ] = {
79
67
for {
80
68
_ <- logger.debug(s " Sending message with ${body.size()} B to exchange $exchangeName with routing key ' $routingKey' and $properties" )
81
69
_ <- blocker.delay {
@@ -102,38 +90,6 @@ class DefaultRabbitMQProducer[F[_], A: ProductConverter](name: String,
102
90
}
103
91
}
104
92
105
- private def sendWithAck (routingKey : String , body : Bytes , properties : MessageProperties , attemptCount : Int , publisherConfirmsConfig : PublisherConfirmsConfig )(
106
- implicit correlationId : CorrelationId ): F [Unit ] = {
107
-
108
- if (attemptCount > publisherConfirmsConfig.sendAttempts) {
109
- F .raiseError(MaxAttempts (" Exhausted max number of attempts" ))
110
- } else {
111
- val messageId = channel.getNextPublishSeqNo
112
- for {
113
- defer <- Deferred .apply[F , Either [Throwable , Unit ]]
114
- _ <- sentMessages.traverse_(_.update(_ + (messageId -> defer)))
115
- _ <- send(routingKey, body, properties)
116
- result <- defer.get
117
- _ <- result match {
118
- case Left (err) =>
119
- val sendResult = if (publisherConfirmsConfig.sendAttempts > 1 ) {
120
- clearProcessedMessage(messageId) >> sendWithAck(routingKey, body, properties, attemptCount + 1 , publisherConfirmsConfig)
121
- } else {
122
- F .raiseError(NotAcknowledgedPublish (s " Broker did not acknowledge publish of message $messageId" , err))
123
- }
124
-
125
- nacked.mark >> sendResult // TODO: markovat kazdy nack nebo az pokud se to nepovede?
126
- case Right (_) =>
127
- acked.mark >> clearProcessedMessage(messageId)
128
- }
129
- } yield ()
130
- }
131
- }
132
-
133
- private def clearProcessedMessage (messageId : Long ): F [Unit ] = {
134
- sentMessages.traverse_(_.update(_ - messageId))
135
- }
136
-
137
93
private def checkSize (bytes : Bytes , routingKey : String )(implicit correlationId : CorrelationId ): F [Unit ] = {
138
94
sizeLimitBytes match {
139
95
case Some (limit) =>
@@ -178,26 +134,4 @@ class DefaultRabbitMQProducer[F[_], A: ProductConverter](name: String,
178
134
}
179
135
}
180
136
181
- private object DefaultConfirmListener extends ConfirmListener {
182
- import cats .syntax .foldable ._
183
- override def handleAck (deliveryTag : Long , multiple : Boolean ): Unit = {
184
- startAndForget {
185
- logger.plainTrace(s " Acked $deliveryTag" ) >> completeDefer(deliveryTag, Right ())
186
- }
187
- }
188
- override def handleNack (deliveryTag : Long , multiple : Boolean ): Unit = {
189
- startAndForget {
190
- logger.plainTrace(s " Not acked $deliveryTag" ) >> completeDefer(deliveryTag, Left (new Exception (s " Message $deliveryTag not acknowledged by broker " )))
191
- }
192
- }
193
-
194
- private def completeDefer (deliveryTag : Long , result : Either [Throwable , Unit ]): F [Unit ] = {
195
- sentMessages.traverse_(_.get.flatMap(_.get(deliveryTag).traverse_(_.complete(result))))
196
- }
197
- }
198
-
199
- }
200
-
201
- object DefaultRabbitMQProducer {
202
- type SentMessages [F [_]] = Ref [F , Map [Long , Deferred [F , Either [Throwable , Unit ]]]]
203
137
}
0 commit comments