AWS SQS - MessageConsumer stops to receive messages after a while - java

My application registers a listeners to a SQS queue (queue itself is populated by a SNS topic).
When I start the application, message consumer is working as expected but after a while it stops to receive any messages. Can it be that consumer is shutting down after a while?
Suggestions or comments would be much appreciated.
SQSConnection:
#Bean
public SQSConnection amazonSQSConnection(
#Value("${aws.access.key}") String accessKey,
#Value("${aws.secret.key}") String secretKey) throws JMSException {
BasicAWSCredentials basicAWSCredentials = new BasicAWSCredentials(accessKey, secretKey);
AmazonSQSClientBuilder client = AmazonSQSClientBuilder
.standard()
.withRegion(Regions.GovCloud)
.withCredentials(new AWSStaticCredentialsProvider(basicAWSCredentials));
SQSConnectionFactory connectionFactory = new SQSConnectionFactory(new ProviderConfiguration(), client);
return connectionFactory.createConnection();
}
Consummer:
#Bean
public MessageConsumer workOrderChangeConsumer(
SQSConnection connection,
WorkOrderKittingService workOrderKittingService,
AuthenticationProvider authProvider,
#Value("${app.user.name}") String appUserName,
#Value("${aws.sqs.workorder.change.queue}") String woChangeQueue) throws JMSException {
Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
Queue queue = session.createQueue(woChangeQueue);
WorkOrderChangeIngestor workOrderChangeIngestor = new WorkOrderChangeIngestor(
workOrderKittingService,
authProvider,
appUserName);
MessageConsumer consumer = session.createConsumer(queue);
consumer.setMessageListener(workOrderChangeIngestor);
connection.start();
return consumer;
}

You're trying to manage the connection lifecycle by yourself.
I recommend you to let spring manage that for you, by using spring-cloud-aws
https://docs.spring.io/spring-cloud-aws/docs/2.2.3.RELEASE/reference/html/#receiving-a-message
You can create a listener through annotations:
#Component
public class MyMessageHandler {
#SqsListener("queueName")
void handle(String message) {
...
throw new MyException("something went wrong");
}
#MessageExceptionHandler(MyException.class)
void handleException(MyException e) {
...
}
}

Related

why rabbitmq message consumption is so slow

Currently I am making logic to consume Message using Rabbitmq. However, contrary to expectations, it takes too long to consume the message.
If you look at the Queued messages graph in the picture above, you can see Unacked and Ready stacking up.
Looking at the message rates below, the publish speed is fast, but the consumer ack speed is too slow.
I'm not sure if the Rabbitmq Configuration I've developed is wrong or if I forgot to set the listener configuration.
The rabbitmq message I receive is a callback message.
Any help would be greatly appreciated.
This is Rabbitmq configuration and RabbitListener configuration
#Configuration
#Profile({ProfileConfig.RABBITMQ})
public class RabbitmqConfig {
#Value("${rabbitmq.queue.name}")
private String queueName;
#Value("${rabbitmq.exchange.name}")
private String exchangeName;
#Value("${rabbitmq.routing.key.callback}")
private String routingKey;
#Value("${rabbitmq.fetch-count}")
private Integer fetchCount;
#Bean
Queue queue() {
return new Queue(queueName, true);
}
#Bean
DirectExchange directExchange() {
return new DirectExchange(exchangeName);
}
#Bean
Binding binding(DirectExchange directExchange, Queue queue) {
return BindingBuilder.bind(queue).to(directExchange).with(routingKey);
}
#Bean
public RabbitListenerContainerFactory<SimpleMessageListenerContainer> prefetchOneContainerFactory(
SimpleRabbitListenerContainerFactoryConfigurer configurer, ConnectionFactory factory)
{
SimpleRabbitListenerContainerFactory simpleFactory = new SimpleRabbitListenerContainerFactory();
configurer.configure(simpleFactory, factory);
simpleFactory.setPrefetchCount(fetchCount);
return simpleFactory;
}
}
#RabbitListener(queues = {"${rabbitmq.queue.name}"}, concurrency = "3", containerFactory = "prefetchOneContainerFactory")
public void receiveMessage(final String message, Channel channel, #Header(AmqpHeaders.DELIVERY_TAG) long tag) {
try {
JSONParser parser = new JSONParser();
JSONObject json = (JSONObject) parser.parse(message);
String messageType = json.get("messageType").toString();
log.debug("Receive Queue Key={}, Message = {}", messageType, message);
AsyncType asyncType = AsyncType.valueOf(messageType);
executeMessage(asyncType, message);
} catch (Exception e) {
traceService.removeTraceId();
traceService.printErrorLog(log, "Fail to deal receive message.", e, PrintStackPolicy.ALL);
} finally {
try {
channel.basicAck(tag, false);
}
catch (IOException e) {
traceService.printErrorLog(log, "Fail to send ack to RabbitMQ", e, PrintStackPolicy.ALL);
}
}
}
The goal is to consume messages to Rabbitmq faster.
However, the current consumption speed is too slow.

How to receive JSON message from a Solace JMS queue, the queue is already created

I am trying to receive JSON messages from a Solace JMS queue but I am not receiving any message. Below is my code
#Service
public class QueueConsumer {
final String QUEUE_NAME = "test.Request.Q.V01";
// Latch used for synchronizing between threads
final CountDownLatch latch = new CountDownLatch(1);
public void run(String... args) throws Exception {
String host = "test.solace.com";
String vpnName = "TEST_VPN";
String username = "testVpn";
String password = "test123";
System.out.printf("QueueConsumer is connecting to Solace messaging at %s...%n", host);
SolConnectionFactory connectionFactory = SolJmsUtility.createConnectionFactory();
connectionFactory.setHost(host);
connectionFactory.setVPN(vpnName);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setDynamicDurables(true);
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, SupportedProperty.SOL_CLIENT_ACKNOWLEDGE);
System.out.printf("Connected to the Solace Message VPN '%s' with client username '%s'.%n", vpnName, username);
Queue queue = session.createQueue(QUEUE_NAME);
MessageConsumer messageConsumer = session.createConsumer(queue);
messageConsumer.setMessageListener(new MessageListener() {
#Override
public void onMessage(Message message) {
try {
if (message instanceof SolaceMsg) {
System.out.printf("TextMessage received: '%s'%n", ((SolaceMsg) message).getClass());
} else {
System.out.println("Message received.");
}
System.out.printf("Message Content:%n%s%n", SolJmsUtility.dumpMessage(message));
message.acknowledge();
latch.countDown(); // unblock the main thread
} catch (JMSException ex) {
System.out.println("Error processing incoming message.");
ex.printStackTrace();
}
}
});
System.out.println("Start receiving messages....");
connection.start();
System.out.println("Awaiting message...");
latch.await();
connection.stop();
messageConsumer.close();
session.close();
connection.close();
}
public static void main(String... args) throws Exception {
new QueueConsumer().run(args);
}
}
My message type is JSON ad below, and I have created a POJO for this.
{
"customerDetails": {
"customerID": "0001234",
"customerName": "John"
}
}
I am getting one warning saying Response - 400 Queue already exists as it is an existing queue, and I am not receiving any messages. What am I doing wrong here?
Your code snippet looks correct. You can log on to the PubSub+ Manager of your event broker to verify that the client is binding to the correct queue and that the messages were successfully published to the queue and are waiting to be consumed. You can also enable Solace JMS API logging to understand more about what the application is doing: https://docs.solace.com/Solace-JMS-API/Code-and-Compile-Guideli.htm

java.lang.ClassCastException: com.tibco.tibjms.TibjmsxSessionImp cannot be cast to javax.jms.QueueSession

When I try to create a JMS session I am getting above mentioned error.Please advice.JMS server using is TIBCO. and I have tibjms.jar and jms.1.1 jars in class path.I am able to create connection. And spring-jms version is 4.1.4-Release version.
String reply = null;
QueueConnection connection = null;
QueueSession requestSession = null;
QueueSession replySession = null;
QueueReceiver receiver = null;
TemporaryQueue replyQueue = null;
try {
connection = (QueueConnection)jmsTemplate.createConnection();
connection.start();
requestSession = (QueueSession) jmsTemplate.createSession(connection);
replySession = (QueueSession) jmsTemplate.createSession(connection);
Queue queue = (Queue)jmsTemplate.getDestination(requestSession, jmsTemplate.getDefaultDestinationName());
replyQueue = replySession.createTemporaryQueue();
TextMessage requestMessage = requestSession.createTextMessage(message);
requestMessage.setJMSReplyTo(replyQueue);
LOGGER.debug("sending request/reply message "
+ jmsTemplate.getConnectionFactory()
+ requestMessage);
QueueSender queueSender = requestSession.createSender(queue);
queueSender.send(requestMessage);
receiver = replySession.createReceiver(replyQueue);
TextMessage replyMessage = (TextMessage)receiver.receive(jmsTemplate.getReceiveTimeout());
if(replyMessage != null){
reply = replyMessage.getText();
}
}catch(JMSException ex) {
What did lead you to creating that abomination? Use the JmsTemplate and JmsMessagingTemplate instead.
Replace your code with this
JmsMessagingTemplate jms = new JmsMessagingTemplate(jmsTemplate);
String reply = jms.convertSendAndReceive(message, String.class);
Or if you want to use a plain JmsTemplate use the sendAndReceive method, drawback is that you need to handle message creation and conversion yourself.
Message response = jmsTemplate.sendAndReceive(new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(message);
}
});
String reply = ((TextMessage) replyMessage).getText();
Either way everything is handled for you. You are using Spring then also take the benefit of that.

Apache Camel send message JMS Consumer receives message

Have Apache Camel simple message route from folder to ActiveMQ topic:
//Create context to create endpoint, routes, processor within context scope
CamelContext context = new DefaultCamelContext();
//Create endpoint route
context.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception
{
from("file:data/outbox").to("activemq:topic:Vadim_Topic");
//from("activemq:topic:TEST").to.to("file:data/outbox");
}
});
context.start();
Thread.sleep(5000);
context.stop();
}
And JMS implementation if Topic Consumer:
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
try {
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//connection.setClientID("12345");
connection.start();
Topic topic = session.createTopic("Vadim_Topic");
MessageConsumer messageConsumer = session.createConsumer(topic);
MessageListener messageListener = new MessageListener() {
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("Received message: " + textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
};
messageConsumer.setMessageListener(messageListener);
} catch (Exception e) {
e.printStackTrace();
}
Can't understand why is my consumer can't recieve messages sent by Camel route??
I guess thet problem is then I need to subscribe my JMS Consumer on messages sent by Camel?
How can I do this if this is the case?
Camel not only allows you to send messages to a topic, it can also very easily read messages from a topic and send it to one of your POJOs.
A route that reads from your topic and sends the messages to a POJO would look like this:
from("activemq:topic:Vadim_Topic").bean(ExampleBean.class);
Camel will figure out which method to call on the POJO depending on the type of message it received, and the available method signatures. See this page for details on using POJO's in camel routes: https://camel.apache.org/bean.html

Get receipt confirmation from the listener in JMS

I'm using Spring JMS and ActiveMQ to send message from a sender to multiple listeners using ActiveMQ Topic (publish/subscribe). So far all listeners can receive message from the sender. But I want to add a functionality that when a particular listener, say listener1, gets the message, listener1 will send a receipt confirmation to the sender. I followed the comment in my old post and created a TemporaryQueue in the sender and used ReplyTo in the sender and receiver to get the confirmation message from the listener to the sender.
My sender class is:
public class CustomerStatusSender {
private JmsTemplate jmsTemplate;
private Topic topic;
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
public void setTopic(Topic topic) {
this.topic = topic;
}
public void simpleSend(final String customerStatusMessage) {
jmsTemplate.send(topic, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
TextMessage message = session.createTextMessage("hello world");
message.setStringProperty("content", customerStatusMessage);
message.setIntProperty("count", 10);
//send acknowledge request to a listener via a tempQueue
Destination tempQueue = session.createTemporaryQueue();
message.setJMSCorrelationID("replyMessage");
message.setJMSReplyTo(tempQueue);
return message;
}
});
}
}
The sender creates a TemporaryQueue for the listener to send back the confirmation message. Then in one of the listeners, I have the following code to send the confirmation message back to the sender:
public class CustomerStatusListener implements SessionAwareMessageListener<Message> {
public void onMessage(Message message, Session session) {
if (message instanceof TextMessage) {
try {
System.out.println("Subscriber 1 got you! The message is: "
+ message.getStringProperty("content"));
//create a receipt confirmation message and send it back to the sender
Message response = session.createMessage();
response.setJMSCorrelationID(message.getJMSCorrelationID());
response.setBooleanProperty("Ack", true);
TemporaryQueue tempQueue = (TemporaryQueue) message.getJMSReplyTo();
MessageProducer producer = session.createProducer(tempQueue);
producer.send(tempQueue, response);
} catch (JMSException ex) {
throw new RuntimeException(ex);
}
} else {
throw new IllegalArgumentException(
"Message must be of type TextMessage");
}
}
}
However, I found that the following line in the Listener class throws an error:
TemporaryQueue tempQueue = (TemporaryQueue) message.getJMSReplyTo();
MessageProducer producer = session.createProducer(tempQueue);
The exception error says:
The destination temp-queue://ID:xyz-1385491-1:2:1 does not exist.
So what's wrong here? I assume that tempQueue created by the sender is available for the listener in the same JMS session. Why the tempQueue object after calling message.getJMSReplyTo() does not return a valid TemporaryQueue?
The other question is: How do I receive the confirmation message in the sender? Should I implements MessageListener interface in the sender in order to receive the confirmation from the listener? Or should I just call receive() method to receive it synchronously?
Thanks for any suggestions!
If you are using spring-jms, why not just use a MessageListenerAdapter as your listener? - he will take care of the replyTo stuff and your listener can be a simple POJO.
In any case, you don't need to cast it to a Tempoarary queue; it's just a destination as far as the listener is concerned.
Checkout the MessageListenerAdapter` javadocs.
Also, you need to create a consumer on the temp queue on the sending side, to receive the reply. If the sending connection is closed, the temp queue will go away.
I ended up using a separate JMS Queue to send the acknowledge message from the listener-1 to the sender. For some reason, the temporaryQueue created by ActiveMQ is not available during the JMS session.

Categories