I am preparing demo programme which listen from the Amazon SQS.Below is my code.
xml config
<aws-messaging:annotation-driven-queue-listener amazon-sqs="sqsClient" max-number-of-messages="10" wait-time-out="20" visibility-timeout="100" />
UserServiceListenr.java
#Configuration
#EnableSqs
#Component
public class UserServiceListenr {
#SqsListener(value = "CMR", deletionPolicy = SqsMessageDeletionPolicy.ON_SUCCESS)
public void myQueueListener(Message message) throws Exception{
try {
System.out.println("Message Listen start");
System.out.println("Message part "+message);
}catch(Exception e){
System.out.println(" message Exception " + e);
}
}
}
I have put 2 messages on SQS queue.when I try to fetch messages using this demo programme Messages shown Messages_in_flight column in my AWS console.but messages not reach to my sqsListener method and after few minutes messages shown to Messages_available column in AWS console.
Below is the Exception I got when run the programme.
QueueMessageHandler:294 - 1 message handler methods found on class com.sophos.cmr.demo.UserServiceListenr: {public void com.sophos.cmr.demo.UserServiceListenr.myQueueListener(java.lang.String) throws java.lang.Exception=org.springframework.cloud.aws.messaging.listener.QueueMessageHandler$MappingInformation#5f0e9815}
so what's going wrong if any clue?
When you see that message move to column Messages_in_flight - it means that the message was picked up by a consumer but the acknowledgment about success handling, for this specific message, still didn`t receive.
So reason could be the next:
1) Error/Exception appears during the handling message from SQS
2) Spring can`t find appropriate ArgumentResolver to convert income message from SQS to your bean. I see you are using your custom bean - 'Message'
You can look through documentation, section 5.2.5
I had a similar issue and was able to solve it. The docs aren't clear on this matter. I am adding it here in case someone sees this later on.
First, when you insert an item into queue, you need to set some additional message properties so Spring knows to marshall it into your bean. If you set Name: contentType Type: String Value: application/json in the GUI when adding a message to queue, then spring will try to marshall it with Jackson.
Second, I wasn't able to get #SqsListener to work with a non String argument unless it was inside a class annotated with #Controller. For non-controller classes, I had to add a second method argument (#Header("SenderId") String senderId worked for me) and then it routed correctly
Related
I have been agonizing over trying to get the Spring Cloud Sleuth traceId to carry over MQ for quite some time. As I understand based on various articles this is currently unsupported.
Yet when I attempted to add "spanId" as a header property on a JMS message I observed that on the MQ listener side the traceId was carried over automatically and I cannot explain why.
Can someone please help me understand why the below code works ?
Note: spring-cloud-starter-sleuth version = 3.0.0, Spring Boot version = 2.4.3
#AllArgsConstructor
public class MQMessagePublisher {
private final JMSTemplate;
private final Tracer tracer;
public void publishDummyMessage(){
TextMessage textMessage = new JMSTextMessage("some data");
Span span = tracer.currentSpan();
message.setStringProperty("spanId", span.context().spanId());
jmsTemplate.convertAndSend("SOME_Q_NAME", message);
}
}
public class MQListener {
#JmsListener(id="listener", destination = "SOME_Q_NAME", concurrency = "2-4")
public void listen(Message message) throws JMSException {
log.info("Span Id: {}", message.getStringProperty("spanId"));
}
}
Apologies for any syntax errors. I did not copy this code from my editor - just wrote it freehand just now but you should be able to replicate my observations with this.
The thing I cant understand is why does this work ? The log statement in the listener clearly carries across the same traceId without any additional work.
UPDATE :
Earlier the question was written incorrectly - its actually the traceId that gets carried over when the 'spanId' is added to the jms message.
I have updated the question above with the correct terms.
I think I have a problem understanding spring cloud messaging and can't find an answer to a "problem" I'm facing.
I have the following setup (using spring-boot 2.0.3.RELEASE).
application.yml
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
virtual-host: /
cloud:
stream:
bindings:
input:
destination: foo
group: fooGroup
fooChannel:
destination: foo
Service class
#Autowired
FoodOrderController foodOrderController;
#Bean
public CommandLineRunner runner() {
return (String[] args) -> {
IntStream.range(0,50).forEach(e -> foodOrderController.orderFood());
};
}
#StreamListener(target = FoodOrderSource.INPUT)
public void processCheapMeals(String meal){
System.out.println("This was a great meal!: "+ meal);
}
#StreamListener(target = FoodOrderSource.INPUT)
public void processCheapMeals1(String meal){
System.out.println("This was a great meal!: "+ meal);
}
FoodOrderController
public class FoodOrderController {
#Autowired
FoodOrderSource foodOrderSource;
public String orderFood(){
var foodOrder = new FoodOrder();
foodOrder.setCustomerAddress(UUID.randomUUID().toString());
foodOrder.setOrderDescription(UUID.randomUUID().toString());
foodOrder.setRestaurant("foo");
foodOrderSource.foodOrders().send(MessageBuilder.withPayload(foodOrder).build());
// System.out.println(foodOrder.toString());
return "food ordered!";
}
}
FoodOrderSource
public interface FoodOrderSource {
String INPUT = "foo";
String OUTPUT = "fooChannel";
#Input("foo")
SubscribableChannel foo();
#Output("fooChannel")
MessageChannel foodOrders();
}
FoodOrderPublisher
#EnableBinding(FoodOrderSource.class)
public class FoodOrderPublisher {
}
The setup is working, with the exception that both StreamListener receive the same messages. So everything get's logged twice. Reading the documentation, it says specifying a group inside the queues bindings, both the listeners will be registered inside the group and only one listener will receive a single message. I know that the example above is not sensible, but I want to mimic a multi-node environment with multiple listeners setup.
Why is the message received by both listeners? And how can I make sure that a message is only received once within a setup group?
According to the documentation, messages should also be auto-acknowledged by default, but I can't find anything that indicates that the messages actually get acknowledged. Am I missing something here?
Here's some screenshots of rabbit admin
Reading the documentation, it says specifying a group inside the queues bindings, both the listeners will be registered inside the group and only one listener will receive a single message.
That is true when the listeners are in different application instances. When there are multiple listeners in the same instance they all get the same message. This is typically used with a condition where each listener can express interest in which meals they are interested in. Documented here.
Basically, the competing consumer is the binding itself which dispatches the message to the actual #StreamListeners in the application.
So, you can't "mimic a multi-node environment with multiple listeners setup" this way.
but I can't find anything that indicates that the messages actually get acknowledged
What do you mean by that? If the message is processed successfully, the container acks the message and it is removed from the queue.
Thow correct answer is already replied on the post, but you can still look into this:
https://github.com/jinternals/spring-cloud-stream
I'm trying to run example from http://www.baeldung.com/spring-remoting-amqp, even when I set up the connection to the dedicated vhost to my RabbitMQ broker, I can only send the request from client (I see it in RabbitMQ UI), but I never get the answer from the server.
The server seems to bean the service (the returning Impl class) with getBeanDefinitionNames(), but I definitly do not see those beans on the client side. I use annotations to set up beans, not the .xml file.
So the question is - why my client is not seeing the Server beans, I discover it more a less in following way:
#Autowired
private ApplicationContext appContext;
public GetResponse get(String id) {
Service service = appContext.getBean(Service.class);
System.out.println(service.ping());
return new GetResponse();
}
The answer which I get on the level of webservice is:
{
"timestamp": "2018-02-01T10:09:00.809Z",
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.remoting.RemoteProxyFailureException",
"message": "No reply received from 'toString' with arguments '[]' - perhaps a timeout in the template?",
"path": "/v3/app/r"
}
Service:
public interface Service extends Serializable{
String ping();
}
Service Impl:
public class ServiceImpl implements Service {
#Override
public String ping() {
System.out.println("ponged");
return "pong";
}
#Override
public String toString() {
return "to string";
}
EDITED + BOUNTY
In the link you can find extracted modules which I want to connect together. I suppose that it is still about 'not seeing' the beans from one module in the second one.
The action can be trigerd with GET http://localhost:8081/v3/app/u The RabbitMQ settings has to be adjusted to your set-up.
https://bitbucket.org/herbatnic/springremotingexample/overview
I think you shouldn't set the routing key in your client, in amqpFactoryBean (and the one you set seems invalid):
https://bitbucket.org/herbatnic/springremotingexample/src/b1f08a5398889525a0b1a439b9bb4943f345ffd1/Mod1/src/main/java/simpleremoting/mod1/messaging/Caller.java?at=master&fileviewer=file-view-default
Did you try to run their example?
https://github.com/eugenp/tutorials/tree/master/spring-remoting/remoting-amqp
Just stumbled upon this question 3 years later.. trying to run the Baeldung example!
I tried debugging the issue and as far as I can tell, something internal in the AMQP implementation of spring remoting is not using the correct Routing Key when sending the client message, meaning the payload arrives at the broker and is never put into the queue for processing, we then timeout after 5s (default) on the client.
I tried the other answer by Syl to remove the routingKey however it doesn't seem to allow us to create a binding without one, and even when creating a binding directly on the broker management page (without a routing key) it doesn't route the messages.
I have not managed to make the example work, however I found a blog post on fatalerrors.org that shows a custom implementation of the AmqpProxyFactoryBean and it has custom handling for the routing key, this one works.
I've create this gist with the example that is working for me in case the blog post above goes under.
One other thing to note is that on the Baeldung example they are using a DirectExchange, while here we are using a TopicExchange.
I working on an application which reads message from Azure service bus. This application was created using spring boot, Spring jms and Qpid jms client. I am able to read the message properly from Queue without any issues. PFB My code which I am using to read message.
#Service
public class QueueReceiver {
#JmsListener(destination = "testing")
public void onMessage(String message) {
if (null != message) {
System.out.println("Received message from Queue: " + message);
}
}}
Issue is we have different destinations for different environemnts, like testing for dev, testing-qa for qa and testing-prod for production, all these values are provided as azure.queueName in different application-(ENV).proerpties respectively. I want to pass these destinations dynamically to the destination in JmsListener Annotation. When i try using
#Value("${azure.queueName}")
private String dest;
and passing dest to annotation like #JmsListener(destination = dest)
I am getting The value for annotation attribute JmsListener.destination must be a constant expression Error. After googling with this Error i found that we cannot pass dynamic value to Annotation. Please help me how to resolve this issue or any other solution for this.
Use
destination="${azure.queueName}"
i.e. put the placeholder in the annotation directly.
You can use a dynamic name as defined in the application.properties file.For Example:
#JmsListener(destination = "${queue.name}")
Since you can't access any class variables here so this is the best option available.
I am currently working on a project involves consuming messages from RabbitMQ brocker. However, I am still new to Spring Integration, AMQP and RabbitMQ.
I have an issue with consuming malformed messages formats. When my consumer receives a malformed message it returns it back the queue then RabbitMQ sends it back which creates an endless cycle.
In Spring Integration documentation there are some configuration that can be implemented to that this kind of message are no returned back to the queue.
However I could not understand how to implement that.
What I want is to be able to configure some kind of bean that has a format like
class ExceptionHandler {
public void handle(Throwable e ) {
Logger.log("Some log ... we don't give a Sh** ... ") ;
}
}
I've checked section 3.9 Exception Handling
and 3.15.3 Message Listeners and the Asynchronous Case
but unfortunately I could not understand anything.
So, if you have an example code or a link to one send it I will be greateful.
Yes, that's is one of the correct solution - to throw AmqpRejectAndDontRequeueException, when you decide that the message should not be requeued.
There is also defaultRequeueRejected on the SimpleMessageListenerContainer, which is true by default.
You maybe should to take a look to the DLX/DLQ solution to not lose those malformed messages.
Please, share the StackTrace which bothers you.
There is such a code in the SimpleMessageListenerContainer:
catch (AmqpRejectAndDontRequeueException rejectEx) {
/*
* These will normally be wrapped by an LEFE if thrown by the
* listener, but we will also honor it if thrown by an
* error handler.
*/
}
After a lot of try-fails attempts I was able to handle the error. However I am struggling with harboring the exception log now. I don't understand why this is implemented this way. I was able to handle the log issue too.
It turns that there is another way to say that you don't want to return the message back it is with acknowledge-mode="NONE" attribute. Checkout 10.2 Inbound Channel Adapter section.This way you don't even need to throw that ugly exception.
< bean id="handler" class="MessagesErrorHandler"/>
< int-amqp:inbound-channel-adapter
error-handler="handler"
id="idActivityAdapter"
channel="channelName"
queue-names="activityQueue"
/>
import org.springframework.util.ErrorHandler;
import org.springframework.amqp.AmqpRejectAndDontRequeueException;
public class MessagesErrorHandler implements ErrorHandler {
#Override
public void handleError(Throwable throwable) {
System.out.println("YESSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS ERROR IS HANDLED !!!!");
throw new AmqpRejectAndDontRequeueException(throwable);// this very important
//so that message don't go back to the queue.
}
}
The AmqpRejectAndDontRequeueException is a signal to the container to reject and not requeue the message; by default, it requeues any exception.
Alternatively, you can manually wire up a SimpleMessageListenerContainer bean; set defaultRequeueRejected to false and add it to the adapter using the container attribute. Then, all exceptions will cause messages to be rejected and not requeued.
Also, instead of an error-handler, you can use an error-channel and throw the AmqpRejectAndDontRequeueException from the error flow.