@rpc to support BROADCAST mode

#1

Just to explain my use-case.

Service A is a HTTP service where it calls rpc called getModels(customer_id)

Service B is an rpc service that will have multiple instance loading different customer_id data. It has a rpc method called getModels(customer_id)

We would like to launch multiple Server B in different containers but each will load different models of customer per model. I would like Service A to call an rpc method in a broadcast model such that ALL containers (Service B) will receive the message. The container that has customer_id data will respond/reply back…

Is there a way to do that ? I know for events_handler has a BROADCAST mode and i have test and it work… but i need it to reply a message back to Service A which will return the HTTP response… i dun think i have a elegant way to do this in Nameko.

Advise or alternative idea will be appreciated !

Thanks

0 Likes

#2

Hi @coddericko,

The pattern you’re describing is called Surveyor-Responder. It isn’t available out of the box, but could be implemented without too much effort.

Nameko RPC is an abstraction on top of normal AMQP publishing and consuming. You can reimplement Nameko RPC or Surveyor-Responder on top of the lower level Publisher and Consumer extensions in nameko.messaging (or the utility Publisher and Consumer in nameko.amqp)

In AMQP, you publish events to an exchange with a routing key. The exchange duplicates that message into every queue bound to that exchange with a matching routing key. Consumers consume messages from queues. If multiple consumers are consuming from the same queue the messages are distributed between them.

Nameko RPC arranges the AMQP entities in the following way:

  1. There is a queue for RPC requests for each service (named simply <service-name>)
  2. The RPC request queue is bound to the RPC exchange (nameko-rpc) with a wildcard routing key (<service-name>.*)
  3. The RPC entrypoint creates a consumer for the RPC request queue from every service instance. This means that RPC requests are distributed amongst all the connected services.
  4. The RPC client publishes requests to the RPC exchange with the routing key <service-name>.<method-name>. It is delivered by the exchange into the matching service queue.
  5. The request message is consumed by one of the service instances, and the entrypoint uses the routing key to determine which method to execute.
  6. The RPC client also creates a consumer and a queue for to receiving the RPC responses. This queue is bound to the RPC exchange with a unique routing key, which is specified as the reply-to header in the RPC requests.
  7. When the service method has executed and a response is available, the RPC entrypoint publishes it back to the RPC exchange with the unique reply-to as its routing key.
  8. The RPC client consumes the response from the reply queue.

You can verify all this by creating a tiny service with an RPC client and entrypoint and poking around in the RabbitMQ management interface. Example service:

from nameko.rpc import rpc, RpcProxy

class Service:
    name = "service"

    service_rpc = RpcProxy("service")

    @rpc
    def method(self):
        return "OK"

Nameko Events use a different arrangement of queues, exchanges and routing keys. This is briefly documented in nameko/events.py. Again, set up a small example and poke around in the RabbitMQ interface and the arrangement should be clear.

For your usecase, I would emit something like a BROADCAST event for the request, and then set up a separate Consumer to receive the replies.

If you study the RPC and Events implementations you should get the idea. It’s been refactored and clarified a lot in the v3.0.0-rc branch, so I’d recommend looking at that.

If you end up building a robust Surveyor-Responder implementation it would be a good candidate for a standalone library.

0 Likes