Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@RabbitListener for the same queue in multiple classes

I was wondering if it is possible in Spring AMQP to receive messages from the same queue in multiple classes depending on the payload type.

I am aware of using the @RabbitListener annotation in class and then putting @RabbitHandler on methods, but I would like to split complexity of message handling in multiple classes while keeping a single queue.

Version currently in use: Spring AMQP v2.0.3 along with RabbitMQ.

like image 676
JoKo Avatar asked Oct 23 '25 19:10

JoKo


2 Answers

Well, it isn't possible. The way you would like it won't be a queue then. That is really an architecture decision to design a single listener and distribute to its methods according the payload type.

As a workaround I can suggest you to delegate the logic from a single @RabbitListener class to those business services:

    @RabbitListener(queues = "foo")
    public class MyListener {

        private final ServiceA serviceA;

        private final ServiceB serviceB;

        public MyListener(ServiceA serviceA, ServiceB serviceB) {
              this.serviceA = serviceA;
              this.serviceB = serviceB;
        }


        @RabbitHandler
        public void handleA(A a) {
             this.serviceA.handle(a);
        }

        @RabbitHandler
        public void handleB(B b) {
             this.serviceB.handle(b);
        }
    }
like image 187
Artem Bilan Avatar answered Oct 26 '25 21:10

Artem Bilan


Yes you can, but it takes a little different approach: You need lo listen to the generic Message Type, do some switching, and your own deserialisation. You can of course completly hide that code somewhere (baseclass, annotations...)

Example below can be extended to listen to whatever types extra. The example above would be to filter on A and B DTO types.

void receive(ADTO dto)
{
    System.out.print(dto.url);
}

void receive(BDTO dto)
{
    System.out.print(dto.url);
}
@RabbitListener(queues = "your.queue.name")
public void listenMesssage(Message message) 
 {
    try 
    {
        String typeId = message.getMessageProperties().getHeaders().get("__TypeId__").toString();
        String contentType = message.getMessageProperties().getContentType();
        if (contentType != "application/json" || typeId == null || !typeId.contains(ADTO.class.toString()))
        {
            //TODO log warning
            System.out.print("type not supported by this service");
            return;
        }
        Object receivedObject = new Jackson2JsonMessageConverter().fromMessage(message);

        if (receivedObject instanceof ADTO)
        {
            receive((ADTO)receivedObject);
            System.out.print("ADTO");
        }
        //else

Alternatively, you can also do the serialisation like this:

....
String typeId = message.getMessageProperties().getHeaders().get("__TypeId__").toString();
byte[] binMsg =  message.getBody();
String strMsg  = new String(binMsg, StandardCharsets.UTF_8);
ObjectMapper mapper = new ObjectMapper();
if (typeId.contains("ADTO"))
{
   receive(mapper.readValue(strMsg, ADTO.class ));
}
else
...            
like image 20
Roland Roos Avatar answered Oct 26 '25 21:10

Roland Roos