How to specify a custom service instance when using ClusterRPCProxy?

From @sbhhbs on Tue Jan 09 2018 02:49:06 GMT+0000 (UTC)

The use case is for example:

  1. the service it self requires a stable hashing, so that, for example, i can routine all traffic of the same user id to the same physical machine’s service instance.
  2. I have a service that is updated, and like to update one service instance to test it. So I can select some user in whitelist and make their traffic always goes to the updated service instance.

My idea is when specify the service, I can always add some tags on it. And when calling the service, i can control if I’d like a service’s with that tag.

Or, is their any way to get a list of existing instances of a service, so that I can call that instance directly?

Copied from original issue: https://github.com/nameko/nameko/issues/500

From @mattbennett on Wed Jan 10 2018 11:02:39 GMT+0000 (UTC)

You can’t do this with the built-in RPC implementation because of the way the message routing is configured. All service instances consume from a single queue and are therefore indistinguishable.

The routing is as follows:

MESSAGE          ROUTING KEY        EXCHANGE     BINDING                  QUEUE                             CONSUMERS


                                   nameko-rpc                                                            +----------------+
                                     +---+                   +-----------------------------+      +------>service instance|
  +-+                                |X X|                   |                             |      |      +----------------+
  |X| +----------------------------> | X +-------------------> {service-name}              +------+
  +-+  {service-name}.{method-name}  |X X|  {service-name}.* |                             |      |      +----------------+
                                     +---+                   +-----------------------------+      +------>service instance|
                                                                                                         +----------------+

In words:

  • Messages are published to the nameko-rpc exchange…
  • With a routing key describing the target service and method name
  • One queue per service is bound to the (topic) RPC exchange with a wildcard binding matching the service name and any method name
  • Therefore all request messages for a target service will be deposited into the queue for that service
  • Finally all instances of the target service consume from the service queue. Messages are consumed in a round-robin.

Hopefully that illustrates why targeting service instances isn’t possible.

You could get your desired behaviour by using a different routing topology. Something like this:

MESSAGE          ROUTING KEY              EXCHANGE     BINDING                  QUEUE                             CONSUMERS


                                         nameko-rpc
                                           +---+                        +-----------------------------+
  +-+                                      |X X|                        |                             |        +----------------+
  |X| +----------------------------------> | X +------------------------> {service-name}-foo          +-------->service instance|  Tags: foo
  +-+  {service-name}.{method-name}.{tag}  |X X|  {service-name}.*.foo  |                             |        +----------------+
                                           +---+-+                      +-----------------------------+-+
                                                 +-+                                                    +-+
                                                   +-+                  +-----------------------------+   +-+
                                                     +-+                |                             |     +-->----------------+
                                                       +----------------> {service-name}-bar          +-------->service instance|  Tags: foo, bar
                                                  {service-name}.*.bar  |                             |        +----------------+
                                                                        +-----------------------------+

This could be implemented as a custom extension that extended the built-in ones.