I was reading RPC-Model in AMQP with RabbitMQ. The tutorial creates a temporary queue and also correlationId. Temporary queues are unique, so why should we need correlationId? I'm a JMS guy, in JMS we do request/response in two ways:
create temporary queue for each request/response
create one response queue and use correlationId and message selector.
can someone explain why do we need both temporary queue and correlationId in AMQP RPC model? It seems AMQP does not have something like message selector. Am I right?
Correct, temporary queues are unique to the client making the RPC request. We could create the RPC client to have a unique queue for each unique request it makes, but that would be inefficient - see the first paragraph of CorrelationId here:
In the method presented above we suggest creating a callback queue for every RPC request. That's pretty inefficient, but fortunately there is a better way - let's create a single callback queue per client.
So a better way is to have one queue which the RPC client gets response back on and uses the correlationId to match the request that the RPC client makes to the result that the RPC server sends back.
...having received a response in that queue it's not clear to which request the response belongs. That's when the correlation_id property is used. We're going to set it to a unique value for every request. Later, when we receive a message in the callback queue we'll look at this property, and based on that we'll be able to match a response with a request. If we see an unknown correlation_id value, we may safely discard the message - it doesn't belong to our requests.
So referencing the Summary section of the RPC tutorial:
when a client starts it creates an exclusive and unique queue
when it sends an RPC request it sets the reply_to which is the queue name (so the server knows which queue to send a response to) and also sets a correlationId which is a unique val for each RPC request
the request is sent to the RPC queue
the RPC worker (or server) receives the request processes it then using the reply_to value sends the response back to the client, it also sets the correlationId
the RPC client waits for a response and when it receives one uses the correlationId to MATCH the response with the request
Related
I have multiple instances of service A deployed where each service produces the request X events to SQS, which triggers the lambda functions and produces the various response events for that request submitted by particular service A instance. I would like to deliver these response events stored in a ActiveMQ topic such that each service A instance should only get the response events of a request it is submitted to queue. I can scale the number of service A instances horizontally.
For example, I have 2 instances of service A named instance1 & instance2. the instance1 submits the request to SQS with request id 1 and the instance2 submits the request to same SQS with request id 2 (request id is unique). The SQS will trigger the lambda functions, will produces the response events for request id 1 & 2 after lambda processing, stores them in a ActiveMQ topic (single one). The instance 1 & 2 are subscribers for this topic and the instance 1 should only get the response events of the request 1 and similary for instance 2.
Can we write a filtering logic for this? or can we fulfil this with Redis pub-sub? Kindly help.
This sounds like a request-reply pattern. There are several patterns for doing request and reply with ActiveMQ.
Shared request destination, separate response destination(s)
Shared request destination, shared response destination(s)
Separate request destination, separate response destination(s)
For #1:
i. Producer sets up a consumer on the response queue (or topic)
ii. Publish requests to a queue (or topic)
iii. Set the name of the response queue (or topic) in the JMSReplyTo header on the message
iv. Consumer receives the request, processes the messages and publishes response to queue (or topic) based on the value from the JMSReplyTo header
For #2:
i. Producer sets up a consumer on the response queue (or topic) using a selector to match the message based on a header value
ii. Publish requests to a queue (or topic)
iii. Consumer receives the request, processes the messages and publishes response to queue (or topic) and sets a value for the header field on the message
For #3:
.. basically the same as the others, but w/ separate request queues.
#1 is generally the most scalable, observable, and preferred option. You can also use 'disappearing' queues for request & response queues using ActiveMQ's destination garbage collection (esp useful for option #3).
Important-- you mentioned 'topic' and 'storing messages'. Keep in mind that topics do not store messages by default in ActiveMQ. You may want a queue instead.
I need to create a RabbitMQ-based microservices solution in Nestjs. To scale the system, I need to deploy several instances (e.g., 3 dockers) of the same microservice (server) listening to the same RabbitMQ topic, processing requests coming from the consumer (client) (see the following figure). I assume these 3 instances use a kind of Round Robin to pop up messages/requests from the queue/topic and process them and then return the results in another queue (i.e., 'callback' queue) to the consumer. I am wondering if
Nestjs already handles the correlation_id (to correlate responses with requests | to tackle the issue, having received a response in that queue it's not clear to which request the response belongs.) under the hood, or do I need to extend the current library?
Does NestJs create an exclusive callback queue (when several instances of the same microservices are deployed) or do I need to take care of that myself in my code (i.e., The worker (aka: server | microservice) is waiting for requests on that queue. When a request appears, it does the job and sends a message with the result back to the Client/Consumer, using the queue from the reply_to field.). In other words, The client (consumer) waits for data on the callback queue. When a message appears, it checks the correlation_id property. If it matches the value from the request it returns the response to the application.
For example, I've implemented a search indexing service, which receives search requests and produces responses using messages.
Currently I've defined a queue to enqueue search requests and other one to enqueue search results.
Would you refactor this to just enqueue to an unique queue where messages have a request and response routing keys? Or is this overusing RabbitMQ on this particular case?
It sounds like you want to use the RPC pattern? To follow the protocol, you should publish the response based on the ReplyTo or ReplyToAddress from the BasicProperties. That way, it is up to the caller (requester) to decided where the response is expected to be published. In my opinion it might be overkill to declare a dedicated exchange for one message type. For higher performance, you could use the direct reply to feature. There are a lot of high level clients that helps you deal with some of these things.
After some research, I feel that this should be a good practice:
There should be a single exchange. For example searchrequest.
Two queues, one for incoming requests and other for responses.
The whole single exchange should route message from requests or responses queue based on a given routing key.
When some service does a search requests, sends a message to searchrequest exchange and request routing key. When search indexing service creates a response, sends it to searchrequest exchange too but it publishes the response message with response routing key.
At the end of the day, using routing keys to publish messages to same queue on my particular case doesn't seem to be natural. It feels hacky.
Take the following sample requirement:
Service A does some work. That work is done periodically and no one asks Service A to do it. It's done automatically.
Service B needs to query data produced by Service A. Service A resides in a different server than Service B.
That is, Service B won't be able to get data if Service A doesn't provide some way of asking it for the data.
I want to require Service A data the SOA way using RabbitMQ: when Service B requires some data, it sends a message to a given exchange and its written to some queue. Then, Service A processes the message and publishes the answer to some other exchange. Finally, Service B listens the answer message and the cycle ends.
My question
I need some way to both publish and consume messages identified by the operation that requested data to Service A, and I also need that each started operation could be identified by an unique identifier.
My question is about how to publish a message and be able to receive an answer for a particular invocation of an operation.
I just want to validate that RabbitMQ routing keys are the answer to this requirement. For example, Service A sends a message with a routing key 072e6ee1-6046-4c3b-bade-9077c863637b. There's a consumer in Service B which consumes any message ignoring the routing key, but once it produces a result, it does publishing a message to an exchange with the same routing key . Therefore, Service A receives message because it's bound to the whole routing key.
Is it a possible right usage of routing keys?
This doesn't seem to be the correct way to use routing keys, if I understand you correctly. Routing keys tend to be used to define an operation, eg 'do-this' or 'do-that' - I wouldn't expect them to contain keys, unless those keys were a limited set that defined how the system operates. The Ids that you mention (they seem to be correlation Ids, so that you can match a received response to an outbound request, asynchronously) would be contained in the body of the message. So, you have the following setup:
Service B -> send message to exchange with routing key 'process-data'. The message contains the Id in its body.
Service A listens to messages on Queue A, which is is bound to the exchange with binding key 'process-data'. It then dequeue's Service B's message, whose body contains the Id
Service A performs processing
Service A -> send message to exchange with routing key 'data-processed'. The message contains the original Id in its body.
Service B listens to messages on Queue B, which is is bound to the exchange with binding key 'data-processed'. It then dequeue's Service A's message, whose body contains the Id
Finally, RabbitMQ already provides a solution to solve my scenario: Remote Procedure Calls tutorial.
At the end of the day, a message can have correlation id and a reply to fields:
Correlation id. An arbitrary value used to uniquely-identify an operation across many exchanges/queues.
Reply to. An arbitrary string to define on which exchange or queue you want to put the result of a given operation. The target exchange/queue will be the callback information that will be received by the operation.
Thus, Service A sends a message to a given exchange with a correlation id and a reply to value. It gets subscribed to reply to exchange. When Service B processes the message it publishes a message to the reply to exchange and Service A receives the whole result.
BTW, I still have some concerns about this model, because you need to receive callback messages from other operations while you wait for the result, and whenever you consume a message that's not marked with desired correlation id, you simply don't process it and you wait for the next one, and so on, until you get what you want.
Maybe it might be a good idea that callback messages would be queued with a time-to-live so if who started an operation isn't waiting for the callback anymore, a given callback message would be automatically dropped.
Another approach would be throwing the callback information as a document or record in a SQL/NoSQL database, and poll the database with an indexed query by callback identifier...
If I find some other approach using RabbitMQ I'll update my answer.
RPC call and cast are two different types of message passing protocol in OpenStack. In case of RPC.call, the invoker (or caller) waits for the reply or ack messsage from the worker (callee).
I am trying to intercept all RPC messages (both Request & Reply Message) passing through rabbitmq system in OpenStack. In OpenStack all request messages pass through a single exchange named "nova". Attaching a new queue to the "nova" exchange, I can capture request Message.
Now, I want to capture reply messages that are sent back to callee. Reply messages can be captured by "direct Consumer" as specified by AMQP and Nova and excerpt as follows
a Direct Consumer comes to life if (an only if) a rpc.call operation is executed; this object is
instantiated and used to receive a response message from the queuing system; Every consumer connects to
a unique direct-based exchange via a unique exclusive queue; its life-cycle is limited to the message
delivery; the exchange and queue identifiers are determined by a *UUID generator*, and are marshaled in
the message sent by the Topic Publisher (only rpc.call operations).
In order to capture reply message, I have tried to connect to a direct exchange with corresponding msg_id or request_id. I am not sure what would be correct exchange id for capturing reply of a specific rpc.call.
Any idea what would be the exchange id what I may use to capture reply from a rpc.call message ? What is the UUID generator as specified in the excerpt I attached ?
I don't know the details of the OpenStack implementation, but when doing RPC over Messaging Systems, usually messages carry a correlation_id identifier that should be used to track requests.
See: http://www.rabbitmq.com/tutorials/tutorial-six-python.html