17
17
import reactor .rabbitmq .BindingSpecification ;
18
18
import reactor .rabbitmq .ExchangeSpecification ;
19
19
20
+ import java .time .Duration ;
21
+ import java .time .Instant ;
20
22
import java .util .HashMap ;
21
23
import java .util .Optional ;
22
24
import java .util .function .Function ;
25
+ import java .util .logging .Level ;
23
26
24
27
import static java .util .Optional .ofNullable ;
25
28
import static org .reactivecommons .async .commons .Headers .*;
@@ -37,12 +40,14 @@ public class ApplicationQueryListener extends GenericMessageListener {
37
40
private final boolean withDLQRetry ;
38
41
private final int retryDelay ;
39
42
private final Optional <Integer > maxLengthBytes ;
43
+ private final boolean discardTimeoutQueries ;
40
44
41
45
42
46
public ApplicationQueryListener (ReactiveMessageListener listener , String queueName , HandlerResolver resolver ,
43
47
ReactiveMessageSender sender , String directExchange , MessageConverter converter ,
44
48
String replyExchange , boolean withDLQRetry , long maxRetries , int retryDelay ,
45
- Optional <Integer > maxLengthBytes , DiscardNotifier discardNotifier , CustomReporter errorReporter ) {
49
+ Optional <Integer > maxLengthBytes , boolean discardTimeoutQueries ,
50
+ DiscardNotifier discardNotifier , CustomReporter errorReporter ) {
46
51
super (queueName , listener , withDLQRetry , maxRetries , discardNotifier , "query" , errorReporter );
47
52
this .retryDelay = retryDelay ;
48
53
this .withDLQRetry = withDLQRetry ;
@@ -52,6 +57,7 @@ public ApplicationQueryListener(ReactiveMessageListener listener, String queueNa
52
57
this .replyExchange = replyExchange ;
53
58
this .directExchange = directExchange ;
54
59
this .maxLengthBytes = maxLengthBytes ;
60
+ this .discardTimeoutQueries = discardTimeoutQueries ;
55
61
}
56
62
57
63
@ Override
@@ -69,11 +75,11 @@ protected Function<Message, Mono<Object>> rawMessageHandler(String executorPath)
69
75
protected Mono <Void > setUpBindings (TopologyCreator creator ) {
70
76
final Mono <AMQP .Exchange .DeclareOk > declareExchange = creator .declare (ExchangeSpecification .exchange (directExchange ).durable (true ).type ("direct" ));
71
77
if (withDLQRetry ) {
72
- final Mono <AMQP .Exchange .DeclareOk > declareExchangeDLQ = creator .declare (ExchangeSpecification .exchange (directExchange + ".DLQ" ).durable (true ).type ("direct" ));
73
- final Mono <AMQP .Queue .DeclareOk > declareQueue = creator .declareQueue (queueName , directExchange + ".DLQ" , maxLengthBytes );
78
+ final Mono <AMQP .Exchange .DeclareOk > declareExchangeDLQ = creator .declare (ExchangeSpecification .exchange (directExchange + ".DLQ" ).durable (true ).type ("direct" ));
79
+ final Mono <AMQP .Queue .DeclareOk > declareQueue = creator .declareQueue (queueName , directExchange + ".DLQ" , maxLengthBytes );
74
80
final Mono <AMQP .Queue .DeclareOk > declareDLQ = creator .declareDLQ (queueName , directExchange , retryDelay , maxLengthBytes );
75
81
final Mono <AMQP .Queue .BindOk > binding = creator .bind (BindingSpecification .binding (directExchange , queueName , queueName ));
76
- final Mono <AMQP .Queue .BindOk > bindingDLQ = creator .bind (BindingSpecification .binding (directExchange + ".DLQ" , queueName , queueName + ".DLQ" ));
82
+ final Mono <AMQP .Queue .BindOk > bindingDLQ = creator .bind (BindingSpecification .binding (directExchange + ".DLQ" , queueName , queueName + ".DLQ" ));
77
83
return declareExchange .then (declareExchangeDLQ ).then (declareQueue ).then (declareDLQ ).then (binding ).then (bindingDLQ ).then ();
78
84
} else {
79
85
final Mono <AMQP .Queue .DeclareOk > declareQueue = creator .declareQueue (queueName , maxLengthBytes );
@@ -82,6 +88,45 @@ protected Mono<Void> setUpBindings(TopologyCreator creator) {
82
88
}
83
89
}
84
90
91
+ @ Override
92
+ protected Mono <AcknowledgableDelivery > handle (AcknowledgableDelivery msj , Instant initTime ) {
93
+ AMQP .BasicProperties messageProperties = msj .getProperties ();
94
+
95
+ boolean messageDoesNotContainTimeoutMetadata = messageProperties .getTimestamp () == null ||
96
+ !messageProperties .getHeaders ().containsKey (REPLY_TIMEOUT_MILLIS );
97
+
98
+ if (messageDoesNotContainTimeoutMetadata || !discardTimeoutQueries ) {
99
+ return super .handle (msj , initTime );
100
+ }
101
+
102
+ return handleWithTimeout (msj , initTime , messageProperties );
103
+ }
104
+
105
+ private Mono <AcknowledgableDelivery > handleWithTimeout (AcknowledgableDelivery msj ,
106
+ Instant initTime ,
107
+ AMQP .BasicProperties messageProperties ) {
108
+ long messageTimestamp = msj .getProperties ().getTimestamp ().getTime ();
109
+ long replyTimeoutMillis = (int ) messageProperties .getHeaders ().get (REPLY_TIMEOUT_MILLIS );
110
+ long millisUntilTimeout = (messageTimestamp + replyTimeoutMillis ) - currentTimestamp ().toEpochMilli ();
111
+ String executorPath = getExecutorPath (msj );
112
+
113
+ if (millisUntilTimeout > 0 ) {
114
+ return super .handle (msj , initTime )
115
+ .timeout (Duration .ofMillis (millisUntilTimeout ), buildTimeOutFallback (executorPath ));
116
+ }
117
+
118
+ return buildTimeOutFallback (executorPath );
119
+ }
120
+
121
+ private Instant currentTimestamp () {
122
+ return Instant .now ();
123
+ }
124
+
125
+ private Mono <AcknowledgableDelivery > buildTimeOutFallback (String executorPath ) {
126
+ return Mono .fromRunnable (() -> log .log (Level .WARNING , String .format ("query with path %s discarded by timeout" ,
127
+ executorPath )));
128
+ }
129
+
85
130
@ Override
86
131
protected String getExecutorPath (AcknowledgableDelivery msj ) {
87
132
return msj .getProperties ().getHeaders ().get (SERVED_QUERY_ID ).toString ();
0 commit comments