senecajs-labs / seneca-rabbitmq-transport

Seneca micro-services message transport over RabbitMQ messaging.
MIT License
15 stars 19 forks source link

An example of properly setting up queue topics #16

Open atoi opened 8 years ago

atoi commented 8 years ago

I'm playing around with the plugin to understand how to use it properly and to see if it suits my needs. My goal is to build a microservice system with components talking to each other by sending messages to the queue.

Let's start from something simple:

const seneca = require('seneca');

seneca()
  .use('rabbitmq-transport')
  .add('role:test, cmd:ping', function(params, done) {
    done(null, { result: 'pong' });
  })
  .listen({ type: 'rabbitmq' });

seneca()
  .use('rabbitmq-transport')
  .client({ type: 'rabbitmq' })
  .act('role:test, cmd:ping', function() {
    console.log(arguments);
  });

Which results to:

Of course, this setup is not suitable for real usage, as each service looks for actions in the same topic. Let's change that, adding a pin option to server transport config:

const seneca = require('seneca');

seneca()
  .use('rabbitmq-transport')
  .add('role:test, cmd:ping', function(params, done) {
    done(null, { result: 'pong' });
  })
  .listen({ type: 'rabbitmq', pin: 'role:test' });

seneca()
  .use('rabbitmq-transport')
  .client({ type: 'rabbitmq' })
  .act('role:test, cmd:ping', function() {
    console.log(arguments);
  });

And the result is:

Hmm, ok. Let's add a pin to client configuration as well, so it generates the same topic names as our server:

const seneca = require('seneca');

seneca()
  .use('rabbitmq-transport')
  .add('role:test, cmd:ping', function(params, done) {
    done(null, { result: 'pong' });
  })
  .listen({ type: 'rabbitmq', pin: 'role:test' });

seneca()
  .use('rabbitmq-transport')
  .client({ type: 'rabbitmq', pin: 'role:test' })
  .act('role:test, cmd:ping', function() {
    console.log(arguments);
  });

And we get somewhat unexpected result:

Ok, let's scratch our head a little and try the following setup, which finally works:

const seneca = require('seneca');

seneca()
  .use('rabbitmq-transport')
  .add('role:test, cmd:ping', function(params, done) {
    done(null, { result: 'pong' });
  })
  .listen({ type: 'rabbitmq', pin: 'role:test' });

seneca()
  .use('rabbitmq-transport')
  .client({ type: 'rabbitmq', pin: 'role:test, cmd:*' })
  .act('role:test, cmd:ping', function() {
    console.log(arguments);
  });

So, the question is: Do I really have to pin client like that? That seems like too much for me. Let's imagine our test microservice has not only cmd, but task, queue and some others types of actions. I would need to setup a client for each of them.

And another related question is: In a case when we have multiple clients talking to the same server and calling the same function, they all listen to the same topic for responses. Is there a way to ensure a client gets a response that 'belongs' to its request and not some other's one? If not, is there a way to tell the server to write to different response topics (one for each client)?

geek commented 8 years ago

The pattern matching in each service should avoid the issue you are pointing to in the first example, right? We may be acknowledging the message too quickly without validating that it matches a known pattern.

Did you try with multiple services and consumers?

nfantone commented 8 years ago

@atoi Take a look at seneca-amqp-transport. It should have the functionality that you need already implemented on it.