简体   繁体   中英

Problem handling Exception thrown by RabbitMq Listener

I have this Listener in my project:

@Service
@RequiredArgsConstructor
@Slf4j
public class ConsumerService {

    @RabbitListener(queues = "${queue.treatment.request}")
    public void handleQueueTreatmentRequestMessageReception(AppointmentPayloadDTO myAppointment) {

        log.info(" ============================  Message received in queue-treatment-plan-new\n: " + myAppointment);

        log.info(" ============================  Creating new treatment plan ....");
    }
}

and this error handler:

@Configuration
public class RabbitMQErrorHandler implements ErrorHandler
{

    @Override
    public void handleError(Throwable t) {
        System.out.println("======================================================================================");
        System.out.println("error occurred in message listener and handled in error handler" + t.toString());
        System.out.println("======================================================================================");
    }
}

My goal here is to handle a MessageConversionException . The message is being send by an other microservice and the exception looks like that:


org.springframework.amqp.rabbit.support.ListenerExecutionFailedException: Listener threw exception
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.wrapToListenerExecutionFailedExceptionIfNeeded(AbstractMessageListenerContainer.java:1693) ~[spring-rabbit-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1583) ~[spring-rabbit-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1498) ~[spring-rabbit-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1486) ~[spring-rabbit-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1477) ~[spring-rabbit-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1421) ~[spring-rabbit-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:963) [spring-rabbit-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:913) [spring-rabbit-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1600(SimpleMessageListenerContainer.java:81) [spring-rabbit-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.mainLoop(SimpleMessageListenerContainer.java:1284) [spring-rabbit-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1190) [spring-rabbit-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_201]
Caused by: org.springframework.amqp.support.converter.MessageConversionException: failed to convert serialized Message content
    at org.springframework.amqp.support.converter.SimpleMessageConverter.fromMessage(SimpleMessageConverter.java:114) ~[spring-amqp-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.amqp.rabbit.listener.adapter.AbstractAdaptableMessageListener.extractMessage(AbstractAdaptableMessageListener.java:302) ~[spring-rabbit-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter$MessagingMessageConverterAdapter.extractPayload(MessagingMessageListenerAdapter.java:323) ~[spring-rabbit-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.amqp.support.converter.MessagingMessageConverter.fromMessage(MessagingMessageConverter.java:122) ~[spring-amqp-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.toMessagingMessage(MessagingMessageListenerAdapter.java:205) ~[spring-rabbit-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:132) ~[spring-rabbit-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1579) ~[spring-rabbit-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    ... 10 common frames omitted
Caused by: java.lang.IllegalArgumentException: Could not deserialize object
    at org.springframework.amqp.utils.SerializationUtils.deserialize(SerializationUtils.java:94) ~[spring-amqp-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.amqp.support.converter.SimpleMessageConverter.fromMessage(SimpleMessageConverter.java:110) ~[spring-amqp-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    ... 16 common frames omitted
Caused by: java.io.InvalidObjectException: enum constant FOOBOO does not exist in class com.hospital.appointment.enums.Disease
    at java.io.ObjectInputStream.readEnum(ObjectInputStream.java:2014) ~[na:1.8.0_201]
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1570) ~[na:1.8.0_201]
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2287) ~[na:1.8.0_201]
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2211) ~[na:1.8.0_201]
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2069) ~[na:1.8.0_201]
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573) ~[na:1.8.0_201]
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431) ~[na:1.8.0_201]
    at org.springframework.amqp.utils.SerializationUtils.deserialize(SerializationUtils.java:91) ~[spring-amqp-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    ... 17 common frames omitted
Caused by: java.lang.IllegalArgumentException: No enum constant com.hospital.appointment.enums.Disease.FOOBOO
    at java.lang.Enum.valueOf(Enum.java:238) ~[na:1.8.0_201]
    at java.io.ObjectInputStream.readEnum(ObjectInputStream.java:2011) ~[na:1.8.0_201]
    ... 24 common frames omitted

So my problem right now is that my ErrorHandler doesn't work at all. Do anyone have an idea what I am doing wrong here?

EDIT#1:

After reading more carefully the spring documentation I found exactly what is not working in my code:

https://docs.spring.io/spring-amqp/reference/html/#exception-handling

Spring says clearly:

However, there is a class of errors where the listener cannot control the behavior. When a message that cannot be converted is encountered (for example, an invalid content_encoding header), some exceptions are thrown before the message reaches user code. ...

Specifically, it rejects messages that fail with the following errors:

osamqp…MessageConversionException: Can be thrown when converting the incoming message payload using a MessageConverter.

And after some lines it defines a solution:

You can configure an instance of this error handler with a FatalExceptionStrategy so that users can provide their own rules for conditional message rejection — for example, a delegate implementation to the BinaryExceptionClassifier from Spring Retry (Message Listeners and the Asynchronous Case). In addition, the ListenerExecutionFailedException now has a failedMessage property that you can use in the decision.

EDIT #2:

After googling it a little bit and following the second approach of @Borislav Stoilov I developed the following solution which works for me. Keep in mind that I changed the annotation of RabbitMQErrorHandler to @Sercvice :

@Configuration
public class RabbitMQConsumerConfiguration implements RabbitListenerConfigurer {


    @Bean
    public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ErrorHandler myRabbitMQErrorHandler,ConnectionFactory connectionFactory,SimpleRabbitListenerContainerFactoryConfigurer configurer) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        configurer.configure(factory, connectionFactory);
        factory.setErrorHandler(myRabbitMQErrorHandler);
        return factory;
    }    
}

@RequiredArgsConstructor
@Slf4j
@Service
public class RabbitMQErrorHandler implements ErrorHandler
{
    @Override
    public void handleError(Throwable t) {
        System.out.println("======================================================================================");
        System.out.println("error occurred in message listener and handled in error handler" + t.toString());
        System.out.println("======================================================================================");

        throw new AmqpRejectAndDontRequeueException("Error Handler converted exception to fatal", t);

    }
}

Have you tried using a Dead Letter queue? It is basically a queue which stores all undelivered messages or errors.

You need to defined it as a bean, something like this

@Bean
Queue deadLetterQueueBean() {
    return QueueBuilder.durable("custom.dead.letter.queue").build();
}

then define listener similar to the one you are already using.

This tutorial worked for me https://docs.spring.io/autorepo/docs/spring-cloud-stream-binder-rabbit-docs/1.1.1.RELEASE/reference/html/rabbit-dlq-processing.html

Option 2: Set errorHandler for the SimpleMessageListenerContainer

class CustomErrorHandler implements ErrorHandler {
    void handleError(Throwable genericError) {
        // do something in case of error
    }
}

@Autowired SimpleMessageListenerContainer 

simpleMessageListenerContainer;

...
simpleMessageListenerContainer.setErrorHandler(new CustomErrorHandler());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM