Georgius17
Georgius17

Reputation: 11

NestJS microservices error with "No matching message handler"

I'm building an application with microservices communicating through RabbitMQ (request-response pattern).
Everything works fine but still I have a problem with error "There is no matching message handler defined in the remote service." -
When I send POST to my Client app, it should simply send the message with data through client (ClientProxy) and the Consumer app should response. This functionality actually works, but always only for the second time. I know it sounds strange but on my first POST request there is always the error from Client and my every second POST request works. However this problem is everywhere in my whole application, so the particular POST request is just for the example.

Here is the code:

Client:

@Post('devices')
async pushDevices(
    @Body(new ParseArrayPipe({ items: DeviceDto }))
    devices: DeviceDto[]
    ) {
    this.logger.log('Devices received'); 
    return this.client.send(NEW_DEVICES_RECEIVED, devices)
}

Consumer:

 @MessagePattern(NEW_DEVICES_RECEIVED)
 async pushDevices(@Payload() devices: any, @Ctx() context: RmqContext) {
    console.log('RECEIVED DEVICES');
    console.log(devices);
    const channel = context.getChannelRef();
    const originalMsg = context.getMessage();
    channel.ack(originalMsg);
    return 'ANSWER';
  }

Client has the RMQ settings with queueOptions: {durable: true} and the consumer as well queueOptions: {durable: true} with noAck: false

Please do you have any ideas what may causes the problem? I have tried sending the data with JSON.stringify and changing the message structure to {data: devices} but the error is still there.

Upvotes: 1

Views: 6991

Answers (6)

Nestor Jaime Perez
Nestor Jaime Perez

Reputation: 1

It is not an error in itself, but rather the type of use that we give to the queue.

For example, if we have created an api gateway and it connects through a queue, we are assuming that this queue will work synchronously because the api gateway receives a request and sends it to the bus, it is received by a microservice and it responds to the request (request-response). However, if we connect another consumer that is only to receive an asynchronous request (event driven), what will happen is that when making a request through the API gateway and receiving it, the microservice will issue a response, but it is very likely that the response will be consumed by the microservice. another consumer and if so, it will be assumed that the (response) has already been given but it will not show us anything in our api gateway.

Recommendation:

Create an exclusive queue for api gateway or for a single service (single consumer) that awaits responses from microservices and another queue only for event driven requests and here there can be multiple consumers without problem.

Upvotes: 0

I solved this issue by placing the @EventPattern decorator on to a @Controller decorator method

Upvotes: 1

Georgius17
Georgius17

Reputation: 11

I've experienced the same error in my another project and after some research I've found out that problem is in the way of distributing messages in RabbitMQ - named round-robin. In my first project I've solved the issue by creating a second queue, in my second project I'm using the package @golevelup/nestjs-rabbitmq instead of default NestJS library, as it is much more configurable. I recommend reading this question

Upvotes: 0

Yi Min Cai
Yi Min Cai

Reputation: 93

I had same error and finally solve it today.
In my project, there is an api-gateway as a hybrid application to receive requests and pass data to other systems, every second request gives an error like below.

error: There is no matching message handler defined in the remote service.

Then I tried to remove the api-gateway hybrid application scope in the code below, the error is gone, hope this helps you out with this.

 // api-gateway main.ts

  const app = await NestFactory.create(AppModule);

  // run as a hybrid app —→ remove it
  app.connectMicroservice({
    transport: Transport.RMQ,
    noACK: false,
    options: {
      urls: [`amqp://${rmqUser}:${rmqPassword}@127.0.0.1:5672`],
      queue: 'main_queue',
      queueOptions: {
        durable: false,
      },
    },
  });

  // run hybrid app
  await app.startAllMicroservices(); —→ remove it
  await app.listen(3000);

Upvotes: 8

Setanjan Roy
Setanjan Roy

Reputation: 3

I encountered this same issue today and could not find any solution online and stumbled upon your question. I solved it in a hacky way and am not sure how it will behave when the application scales.

I basically added one @EventPattern (@MessagePattern in your case) in the controller of the producer microservice itself. And I called the client.emit() function twice.

So essentially the first time it gets consumed by the function that is in the producer itself and the second emit actually goes to the actual consumer. This way only one POST call is sufficient.

Producer Controller:

@EventPattern('video-uploaded')
  async test() {
    return 1;
  }

Producer client :

async publishEvent(data: VideosDto) {
    this.client.emit('video-uploaded', data);
    this.client.emit('video-uploaded', data);
}

Upvotes: 0

molaro
molaro

Reputation: 520

I had this error while NOT using RabbitMQ. I found very little help online around this error message outside of it being related to RabbitMQ.

For me it was an issue where I was importing a DTO from another microservice in my microservice's Controller. I had a new DTO in my microservice that has a similar name to one in another microservice. I accidentally selected the wrong one from the automated list.

Since there wasn't any real indicator that my build was bad, just this error, I wanted to share in case others made the same mistake I did.

Upvotes: 0

Related Questions