In a cloud computing environment, it is often necessary to use the computing resources of other machines, and we may be able to assign a subset of the compute tasks to other nodes when we receive the message for processing. So, how does RABBITMQ use RPC? In this article, we will use the other nodes to find the Fibonacci completion example.
1. Client Interface Clients interface
To show how an RPC service is used, we will create a very simple client class. It will provide a function called call, which sends the RPC request and blocks the result of knowing that the RPC operation was received. The code is as follows:
[Python]View Plaincopy
- Fibonacci_rpc = Fibonaccirpcclient ()
- result = Fibonacci_rpc.call (4)
- Print "FIB (4) is%r"% (result,)
2. callback function queues Callback queue
In general, it is easier to make RPC remote calls in RABBITMQ. The client sends a message to the request and the server returns the response result. In order to receive responses, the client needs to provide a "callback" (callback) queue address when publish message. Code is as follows:
[Python]View Plaincopy
- result = Channel.queue_declare (exclusive=True)
- Callback_queue = Result.method.queue
- Channel.basic_publish (exchange=",
- routing_key=' Rpc_queue ',
- Properties=pika. Basicproperties (
- Reply_to = Callback_queue,
- ),
- Body=request)
- # ... and some code to read a response message from the Callback_queue ...
2.1 Message Properties
AMQP has pre-defined 14 attributes. Most of them are seldom used. The following are usually used in more than one:
- Delivery_mode: Persists a message (by setting a value of 2). Any other value is non-persistent. Please take a RABBITMQ message Queue (iii): task distribution mechanism
- Content_Type: Describes the encoding of Mime-type. For example, set to JSON encoding: Set the property to Application/json.
- Reply_to: Typically used to indicate a queue for callbacks (commonly used to name a callback queue).
- CORRELATION_ID: The RPC response (correlate RPC responses with requests) is associated with the request.
3. Correlation ID Correlation ID
In the last section, the implementation method is to create a callback queue for each RPC request. This is not efficient. Fortunately, here's a workaround: Create a unique callback queue for each client.
This has another problem: it cannot determine if it is a response after it has been received, because all responses are written to the same queue. The correlation_id in the previous section is useful in this case: for each request, a unique value is set, and after the response is received, the value can be used to determine whether it is its own response. If it is not your own response, do not deal with it.
4. Summary
Work Flow:
- When the client starts, it creates an anonymous exclusive callback queue.
- The client's RPC request will be set at the same time two properties: reply_to set to callback queue;correlation_id set to a unique value for each request.
- The request will be sent to an rpc_queue queue.
- The RPC side, or server, has been waiting for the queue request. When the request arrives, it replies a message to the client by the queue specified in reply_to.
- The client waits for callback queue data. When the message arrives, it checks the value of the correlation_id and returns the response if the value is consistent with the request it sent.
5. Final realization
The code for rpc_server.py:
[Python]View Plaincopy
- #!/usr/bin/env python
- Import Pika
- Connection = Pika. Blockingconnection (Pika. Connectionparameters (
- host=' localhost '))
- Channel = Connection.channel ()
- Channel.queue_declare (queue=' rpc_queue ')
- def fib (n):
- if n = = 0:
- return 0
- elif N = = 1:
- return 1
- Else:
- return fib (n1) + FIB (n-2)
- def on_request (ch, method, props, body):
- n = Int (body)
- print "[.] FIB (%s) "% (n,)"
- Response = FIB (n)
- Ch.basic_publish (exchange=",
- Routing_key=props.reply_to,
- Properties=pika. Basicproperties (correlation_id = \
- PROPS.CORRELATION_ID),
- BODY=STR (response))
- Ch.basic_ack (Delivery_tag = Method.delivery_tag)
- Channel.basic_qos (prefetch_count=1)
- Channel.basic_consume (on_request, queue=' rpc_queue ')
- Print "[x] awaiting RPC requests"
- Channel.start_consuming ()
The server code is rather straightforward:
- (4) As usual we start by establishing the connection and declaring the queue.
- (one) We declare our Fibonacci function. It assumes only valid positive integer input. (Don ' t expect this one-to-work for big numbers, it's probably the slowest recursive implementation possible).
- (+) We declare a callback for Basic_consume, the core of the RPC server. It ' s executed when the request is received. It does the work and sends the response back.
- (+) We might want to run more than one server process. In order to spread the load equally through multiple servers we need to set theprefetch_count setting.
The code for rpc_client.py:
[Python]View Plaincopy
- #!/usr/bin/env python
- Import Pika
- Import UUID
- Class Fibonaccirpcclient (object):
- def __init__ (self):
- self.connection = Pika. Blockingconnection (Pika. Connectionparameters (
- host=' localhost '))
- Self.channel = self.connection.channel ()
- result = Self.channel.queue_declare (exclusive=True)
- self.callback_queue = Result.method.queue
- Self.channel.basic_consume (self.on_response, no_ack=True,
- queue=self.callback_queue)
- def on_response (self, ch, method, props, body):
- if self.corr_id = = props.correlation_id:
- Self.response = Body
- def call (self, n):
- self.response = None
- self.corr_id = str (UUID.UUID4 ())
- self.channel.basic_publish (exchange=",
- routing_key=' Rpc_queue ',
- Properties=pika. Basicproperties (
- reply_to = Self.callback_queue,
- correlation_id = self.corr_id,
- ),
- BODY=STR (n))
- while self.response is None:
- self.connection.process_data_events ()
- return int (self.response)
- Fibonacci_rpc = Fibonaccirpcclient ()
- Print "[x] requesting FIB (+)"
- Response = Fibonacci_rpc.call (+)
- Print "[.] Got%r "% (response,)
The client code is slightly more involved:
- (7) We establish a connection, channel and declare a exclusive ' callback ' queue for replies.
- (+) We subscribe to the ' callback ' queue, so that We can receive RPC responses.
- The ' On_response ' callback executed on every response are doing a very simple job, for every response message it check s if the correlation_id is the onewe ' re looking for. If So, it saves the response inSelf.response and breaks the consuming loop.
- Next, we define our main call method-it does the actual RPC request.
- In the This method, first we generate a unique correlation_id number and save It-the ' On_response ' callback Functio n would use this value to catch the appropriate response.
- Next, we publish the request message, with the properties: reply_to and correlation_id.
- At this point we can sit back and wait until the proper response arrives.
- () and finally we return the response to the user.
Start rpc_server.py:
[Python]View Plaincopy
- $ python rpc_server.py
- [x] Awaiting RPC requests
To request the number of Fibonacci through the client:
[Python]View Plaincopy
- $ python rpc_client.py
- [x] Requesting fib (+)
The design is not unique now, but this implementation has the following advantages:
- How RPC server is too slow, you can extend it: Start another RPC server.
- On the client side, no lock can be synchronized operation, what he did is to send a request to wait for a response.
Our code is quite simple and does not attempt to solve more complex and important problems, such as:
- What does the client need to do if no server is running?
- Should RPC set a timeout mechanism?
- If the server runs out of error and throws an exception, do you need to forward the problem to the client?
- Do you need a boundary check?
Turn:
Http://www.rabbitmq.com/tutorials/tutorial-six-dotnet.html (official website)
http://blog.csdn.net/anzhsoft/article/details/19633107 (translation)
RABBITMQ Message Queuing (vii): Remote Call (RPC) for cloud cluster [go]