Spring AMQP publisher confirms in a amqp outbound gateway - spring-amqp

I am using spring amqp publishing my messages to RabbitMQ using an outbound gateway. I have set publisher confirms on the connection factory and added my custom callback listener.
The problem is that my CorrelationData is always null and i can't add any correlation data on an outbound gateway. This is only applicable for an outbound channel adapter.
For an outbound gateway will publisher confirms even work?
EDIT
My configuration is below. I looked through the SI code and yes, publisher confirms, are enabled. The problem is what I do when I receive a NACK?
Because of the outbound gateway I don't need a correlation id to handle the response, there is already a thread listening on a temporary reply queue for the response.
What exactly is the point of using publisher confirms with an outbound gateway? If no response is coming or my Rabbit nodes go down I will encounter exceptions. Is there a scenario when I will lose messages?
<rabbit:connection-factory id="rabbitConnectionFactory"
host="someip" port="5672"
username="username"
password="password"
virtual-host="vhost"
publisher-confirms="true"/>
<rabbit:admin connection-factory="rabbitConnectionFactory"/>
<rabbit:template id="amqpTemplate" connection-factory="rabbitConnectionFactory"
confirm-callback="messagesConfirmCallback"/>
<int-amqp:outbound-gateway
request-channel="channel"
amqp-template="amqpTemplate"
exchange-name="exchange"
routing-key-expression="headers['queueSpecific']+'.queue'">
<amqp:request-handler-advice-chain>
<ref bean="retryAdvice"/>
</amqp:request-handler-advice-chain>
</int-amqp:outbound-gateway>
And my callback is also simple
#Component
public class MessagesConfirmCallback implements RabbitTemplate.ConfirmCallback {
private final static Logger LOGGER = LoggerFactory.getLogger(MessagesConfirmCallback.class);
#Override
public void confirm(CorrelationData correlationData, boolean ack) {
if(ack){
LOGGER.info("ACK received");
}
else{
LOGGER.info("NACK received");
}
}
}
This

Unfortunately, I don't see an easy work around with the gateway; the underlying RabbitTemplate only supports adding correlation data on send() methods, not the sendAndReceive methods.
The two options I can think of is to (1) use a pair of outbound and inbound adapters (instead of the gateway), but you'll have to do your own request/reply correlation in that case.
Alternatively (2), use the RabbitTemplate.execute() and in the doInRabbit callback, add code similar to that in the RabbitTempalate.doSendAndReceive, while setting the correlation data as is done in doSend().
I opened a JIRA Issue.

Related

Artemis broker Intercept mqtt client connection

I added a mqtt interceptor into my artemis broker in order to intercept mqtt client connection:
public class SimpleMQTTInterceptor implements MQTTInterceptor
{
#Override
public boolean intercept(final MqttMessage mqttMessage, RemotingConnection connection) throws ActiveMQException
{
System.out.println("MQTT Interceptor gets called ");
if (mqttMessage instanceof MqttConnectMessage)
{
System.out.println("MQTT connection intercepted ");
}
return true;
}
My client apache paho connect to the broker via this port "ws://0.0.0.0:61614".
My problem is that only message published to topics are intercepted.
Why this doesn't intercept CONNECT message ?
The current version of ActiveMQ Artemis, 2.2.0, at the time I write this response, only supports intercepting MQTT Publish control packets. I opened a pull request adding that feature, therefore, it should be present on future versions.

Fetch message details in Spring RecoveryCallback

I'm publishing messages into RabbitMQ and I would like to track the errors when RabbitMQ is down, for this I added one RetryTemplate with the recovery callback, but the recovery callback only provides this method getLastThrowable() and I'm not sure how to provide the details of the messages that failed when RabbitMQ is down. (as per documentation "The RecoveryCallback is somewhat limited in that the retry context only contains the
lastThrowable field. For more sophisticated use cases, you should use an external
RetryTemplate so that you can convey additional information to the RecoveryCallback via
the context’s attributes") but I don't know how to do that, if anyone could help me with one example that will be awesome.
Rabbit Template
public RabbitTemplate rabbitMqTemplate(RecoveryCallback publisherRecoveryCallback) {
RabbitTemplate r = new RabbitTemplate(rabbitConnectionFactory);
r.setExchange(exchangeName);
r.setRoutingKey(routingKey);
r.setConnectionFactory(rabbitConnectionFactory);
r.setMessageConverter(jsonMessageConverter());
RetryTemplate retryTemplate = new RetryTemplate();
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(500);
backOffPolicy.setMultiplier(10.0);
backOffPolicy.setMaxInterval(10000);
retryTemplate.setBackOffPolicy(backOffPolicy);
r.setRetryTemplate(retryTemplate);
r.setRecoveryCallback(publisherRecoveryCallback);
return r;
}
Recovery Callback
#Component
public class PublisherRecoveryCallback implements RecoveryCallback<AssortmentEvent> {
#Override
public AssortmentEvent recover(RetryContext context) throws Exception {
log.error("Error publising event",context.getLastThrowable());
//how to get message details here??
return null;
}
}
AMQP Outbound Adapter
return IntegrationFlows.from("eventsChannel")
.split()
.handle(Amqp.outboundAdapter(rabbitMqTemplate)
.exchangeName(exchangeName)
.confirmCorrelationExpression("payload")
.confirmAckChannel(ackChannel)
.confirmNackChannel(nackChannel)
)
.get();
The isn't possible because the function RabbitTemplate.execute() is already not aware about message you send, because it may be performed from any other method, where we might not have messages to deal:
return this.retryTemplate.execute(
(RetryCallback<T, Exception>) context -> RabbitTemplate.this.doExecute(action, connectionFactory),
(RecoveryCallback<T>) this.recoveryCallback);
What I suggest you to do is like storing message to the ThreadLocal before send and get it from there from your custom RecoveryCallback.

Why XMLMessageProducer is closed when called by multiple threads?

I encapsulated JCSMP API in a class and provide methods for other classes to send and consume messages.
I have the following method:
public void send(byte[] data, String queueName) throws JCSMPException {
// Acquire a message producer
if (producer == null) { //producer is an instance of XMLMessageProducer
producer = session.getMessageProducer(new PublishCallback());
}
Queue queue = JCSMPFactory.onlyInstance().createQueue(queueName);
BytesMessage msg = JCSMPFactory.onlyInstance().createMessage(BytesMessage.class);
msg.setData(data);
msg.setDeliveryMode(DeliveryMode.PERSISTENT);
logger.info("Sending to \"{}\"", queueName);
producer.send(msg, queue);
}
When I have multiple threads calling this method, once in a while, I notice the following exception:
com.solacesystems.jcsmp.ClosedFacilityException: Tried to perform operation on a closed XML message producer
I wonder whether the producer is closed after each call to send(). How should I make this method thread-safe?
Thank you.
XMLMessageProducer does not close itself after each call to send.
The first step here is to investigate why your XMLMessageProducer is closed. The easiest option to do this is to enable the Solace API logging to INFO (or even DEBUG), and edit your question to include the Solace API logs prior to the first ClosedFacilityException.
One possible reason is that your application has disconnected, but was not able to automatically reconnect to the Solace appliance/VMR.

check status of RabbitMQ, whether message is published?

I have one resque job which is run at some event which finally publishes the message to RabbitMQ's exchange, how can I check in bunny(Rabbit MQ ruby client) that whether the message has been successfully published?
Using Acknowledgment or any way?
Thanks in advance!
When you execute the publish you are not sure that the message is published on the queue.
If you want to be sure you have to use you have to use publish confirm or tx transaction.
Read this post http://www.rabbitmq.com/blog/2011/02/10/introducing-publisher-confirms/
Note: By default the clients don't have any HA policy, you have to implement it. See the section Streaming Lightweight Publisher Confirms:
private volatile SortedSet<Long> unconfirmedSet =
Collections.synchronizedSortedSet(new TreeSet());
...
ch.setConfirmListener(new ConfirmListener() {
public void handleAck(long seqNo, boolean multiple) {
if (multiple) {
unconfirmedSet.headSet(seqNo+1).clear();
} else {
unconfirmedSet.remove(seqNo);
}
}
public void handleNack(long seqNo, boolean multiple) {
// handle the lost messages somehow
}
});
Note2: the message is never "put" inside an exchange, but always inside a queue.
Once the publish method returns, the message has published to the queue. There is no deferred action to publishing a message.

how to stop Spring AMQP annotated message listener from receiving messages

when my Spring AMQP message listener recognizes an error, I want to stop receiving messages from the queue. When I have a ListenerContainer configured as bean, I can call stop() on it.
Can I do something similar, when I have configured my listener with an endpoint annotation? E.g. is it possible to inject the ListenerContainer the container has created for me?
thx,
tchick
Please, find #RabbitListener#id() JavaDocs:
/**
* The unique identifier of the container managing for this endpoint.
* <p>If none is specified an auto-generated one is provided.
* #return the {#code id} for the container managing for this endpoint.
* #see org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry#getListenerContainer(String)
*/
String id() default "";
So, you have to inject to your target service the RabbitListenerEndpointRegistry and use it to get access to desired ListenerContainer by its id.

Resources