Skip to content

Commit 84ca73e

Browse files
marknorkinartembilan
authored andcommitted
Improve rabbit template
* Remove double synchronized * Polish `receiveForReply` Change `receiveMessage` from method argument to local variable since it's always passed as null * Remove exception casting in `consumerDelivery` Since `doExecute()` eventually will still wrap all exceptions using `RabbitExceptionTranslator.convertRabbitAccessException(e)` As now exceptions are being re-thrown move consumer cancelling logic inside finally block * Guard `setReplyAddress()` Use same lock as `evaluateFastReplyTo()` is guarded via `RabbitTemplate` lock in `doSendAndReceive()` * Remove locking on `publisherConfirmChannels` map When getting unconfirmed messages count/correlation data remove locking on entire map since concurrent map iterator's are thread safe and underlying Channel's implementation (`PublisherCallbackChannelImpl`) already use synchronization in expire and `getPendingConfirmsCount()` * Extract common consumer canceling logic * Access `directReplyToContainers` map only via lock * remove double checking for null container * change copyright * Polishing - PR comments * Revert "Access directReplyToContainers map only via lock" This reverts commit 489df24
1 parent 18b2090 commit 84ca73e

File tree

1 file changed

+37
-50
lines changed

1 file changed

+37
-50
lines changed

spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java

Lines changed: 37 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -135,6 +135,7 @@
135135
* @author Gary Russell
136136
* @author Artem Bilan
137137
* @author Ernest Sadykov
138+
* @author Mark Norkin
138139
* @since 1.0
139140
*/
140141
public class RabbitTemplate extends RabbitAccessor implements BeanFactoryAware, RabbitOperations, MessageListener,
@@ -341,7 +342,7 @@ public String getEncoding() {
341342
* exchange and routing key.
342343
* @param replyAddress the replyAddress to set
343344
*/
344-
public void setReplyAddress(String replyAddress) {
345+
public synchronized void setReplyAddress(String replyAddress) {
345346
this.replyAddress = replyAddress;
346347
this.evaluatedFastReplyTo = false;
347348
}
@@ -726,13 +727,11 @@ public Collection<String> expectedQueueNames() {
726727
*/
727728
public Collection<CorrelationData> getUnconfirmed(long age) {
728729
Set<CorrelationData> unconfirmed = new HashSet<>();
729-
synchronized (this.publisherConfirmChannels) {
730-
long cutoffTime = System.currentTimeMillis() - age;
731-
for (Channel channel : this.publisherConfirmChannels.keySet()) {
732-
Collection<PendingConfirm> confirms = ((PublisherCallbackChannel) channel).expire(this, cutoffTime);
733-
for (PendingConfirm confirm : confirms) {
734-
unconfirmed.add(confirm.getCorrelationData());
735-
}
730+
long cutoffTime = System.currentTimeMillis() - age;
731+
for (Channel channel : this.publisherConfirmChannels.keySet()) {
732+
Collection<PendingConfirm> confirms = ((PublisherCallbackChannel) channel).expire(this, cutoffTime);
733+
for (PendingConfirm confirm : confirms) {
734+
unconfirmed.add(confirm.getCorrelationData());
736735
}
737736
}
738737
return unconfirmed.size() > 0 ? unconfirmed : null;
@@ -744,12 +743,10 @@ public Collection<CorrelationData> getUnconfirmed(long age) {
744743
* @since 2.0
745744
*/
746745
public int getUnconfirmedCount() {
747-
synchronized (this.publisherConfirmChannels) {
748-
return this.publisherConfirmChannels.keySet()
749-
.stream()
750-
.mapToInt(channel -> ((PublisherCallbackChannel) channel).getPendingConfirmsCount(this))
751-
.sum();
752-
}
746+
return this.publisherConfirmChannels.keySet()
747+
.stream()
748+
.mapToInt(channel -> ((PublisherCallbackChannel) channel).getPendingConfirmsCount(this))
749+
.sum();
753750
}
754751

755752
@Override
@@ -788,11 +785,9 @@ protected void doStop() {
788785
@Override
789786
public boolean isRunning() {
790787
synchronized (this.directReplyToContainers) {
791-
synchronized (this.directReplyToContainers) {
792-
return this.directReplyToContainers.values()
793-
.stream()
794-
.anyMatch(AbstractMessageListenerContainer::isRunning);
795-
}
788+
return this.directReplyToContainers.values()
789+
.stream()
790+
.anyMatch(AbstractMessageListenerContainer::isRunning);
796791
}
797792
}
798793

@@ -1138,19 +1133,18 @@ public <R, S> boolean receiveAndReply(String queueName, ReceiveAndReplyCallback<
11381133
private <R, S> boolean doReceiveAndReply(final String queueName, final ReceiveAndReplyCallback<R, S> callback,
11391134
final ReplyToAddressCallback<S> replyToAddressCallback) throws AmqpException {
11401135
return execute(channel -> {
1141-
Message receiveMessage = null;
1142-
1143-
receiveMessage = receiveForReply(queueName, channel, receiveMessage);
1136+
Message receiveMessage = receiveForReply(queueName, channel);
11441137
if (receiveMessage != null) {
11451138
return sendReply(callback, replyToAddressCallback, channel, receiveMessage);
11461139
}
11471140
return false;
11481141
}, obtainTargetConnectionFactory(this.receiveConnectionFactorySelectorExpression, queueName));
11491142
}
11501143

1151-
private Message receiveForReply(final String queueName, Channel channel, Message receiveMessage) throws Exception {
1144+
private Message receiveForReply(final String queueName, Channel channel) throws Exception {
11521145
boolean channelTransacted = isChannelTransacted();
11531146
boolean channelLocallyTransacted = isChannelLocallyTransacted(channel);
1147+
Message receiveMessage = null;
11541148
if (RabbitTemplate.this.receiveTimeout == 0) {
11551149
GetResponse response = channel.basicGet(queueName, !channelTransacted);
11561150
// Response can be null in the case that there is no message on the queue.
@@ -1189,7 +1183,7 @@ else if (channelTransacted) {
11891183

11901184
private Delivery consumeDelivery(Channel channel, String queueName, long timeoutMillis) throws Exception {
11911185
Delivery delivery = null;
1192-
Throwable exception = null;
1186+
RuntimeException exception = null;
11931187
CompletableFuture<Delivery> future = new CompletableFuture<>();
11941188
DefaultConsumer consumer = createConsumer(queueName, channel, future,
11951189
timeoutMillis < 0 ? DEFAULT_CONSUME_TIMEOUT : timeoutMillis);
@@ -1202,34 +1196,20 @@ private Delivery consumeDelivery(Channel channel, String queueName, long timeout
12021196
}
12031197
}
12041198
catch (ExecutionException e) {
1205-
this.logger.error("Consumer failed to receive message: " + consumer, e.getCause());
1206-
exception = e.getCause();
1199+
Throwable cause = e.getCause();
1200+
this.logger.error("Consumer failed to receive message: " + consumer, cause);
1201+
exception = RabbitExceptionTranslator.convertRabbitAccessException(cause);
1202+
throw exception;
12071203
}
12081204
catch (InterruptedException e) {
12091205
Thread.currentThread().interrupt();
12101206
}
12111207
catch (TimeoutException e) {
12121208
// no result in time
12131209
}
1214-
try {
1210+
finally {
12151211
if (exception == null || !(exception instanceof ConsumerCancelledException)) {
1216-
channel.basicCancel(consumer.getConsumerTag());
1217-
}
1218-
}
1219-
catch (Exception e) {
1220-
if (this.logger.isDebugEnabled()) {
1221-
this.logger.debug("Failed to cancel consumer: " + consumer, e);
1222-
}
1223-
}
1224-
if (exception != null) {
1225-
if (exception instanceof RuntimeException) {
1226-
throw (RuntimeException) exception;
1227-
}
1228-
else if (exception instanceof Error) {
1229-
throw (Error) exception;
1230-
}
1231-
else {
1232-
throw new AmqpException(exception);
1212+
cancelConsumerQuietly(channel, consumer);
12331213
}
12341214
}
12351215
return delivery;
@@ -1606,16 +1586,23 @@ public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProp
16061586
}
16071587
finally {
16081588
this.replyHolder.remove(messageTag);
1609-
try {
1610-
channel.basicCancel(consumerTag);
1611-
}
1612-
catch (Exception e) {
1613-
}
1589+
cancelConsumerQuietly(channel, consumer);
16141590
}
16151591
return reply;
16161592
}, obtainTargetConnectionFactory(this.sendConnectionFactorySelectorExpression, message));
16171593
}
16181594

1595+
private void cancelConsumerQuietly(Channel channel, DefaultConsumer consumer) {
1596+
try {
1597+
channel.basicCancel(consumer.getConsumerTag());
1598+
}
1599+
catch (Exception e) {
1600+
if (this.logger.isDebugEnabled()) {
1601+
this.logger.debug("Failed to cancel consumer: " + consumer, e);
1602+
}
1603+
}
1604+
}
1605+
16191606
protected Message doSendAndReceiveWithFixed(final String exchange, final String routingKey, final Message message,
16201607
final CorrelationData correlationData) {
16211608
Assert.state(this.isListener, () -> "RabbitTemplate is not configured as MessageListener - "

0 commit comments

Comments
 (0)