Rabbitmq (Message Queue) Tutorial 07 RPC

Source: Internet
Author: User

(Using the Java client)

I. Overview

In the work queue chapter, we learned how to use the work queue to allocate time-consuming tasks to multiple workers, but if we need to run a function on a remote computer, this is a completely different scenario, which is usually called RPC.

In this chapter, we will use rabbitmq to build an RPC system: a remote client and an extensible RPC server. We do not have to allocate any time-consuming tasks, we will create a virtual RPC service to return the number of Fibonacci.

1.1 client interface)

To illustrate how an RPC service can be used, we will create a simple client class, which will send an RPC request and receive block through the call of the method name for a reply:

FibonacciRpcClient fibonacciRpc = new FibonacciRpcClient();   String result = fibonacciRpc.call("4");System.out.println( "fib(4) is " + result);
Note:

Although RPC is a very common model in the computer field, it is often criticized when the program does not know whether it is a slow RPC call function, such as debugging in an unpredictable interface system, it increases unnecessary complexity, rather than simplifying software. misuse can cause unrecoverable code. If you want to use it, remember to consider the following suggestions:

1. Clearly distinguish whether the called function is local or remote.

2. The dependency between your file system and components is clear.

3. handling problems? The customer should know what to do when the RPC server fails.

1.2. Callback queue)

In general, it is relatively simple to use rabbitmq to implement rpc. When the client sends a request message and the server responds to the message, we need to send a callback queue address in the request to receive the response, we can use the default queue. Let's try:

callbackQueueName = channel.queueDeclare().getQueue();BasicProperties props = new BasicProperties                            .Builder()                            .replyTo(callbackQueueName)                            .build();channel.basicPublish("", "rpc_queue", props, message.getBytes());// ... then code to read a response message from the callback_queue ...
Note:

Amqp presets a set of 14 attributes for a message. Most of the attributes are rarely used, with the following exceptions:

1. deliverymode: indicates the persistence (value: 2) or status (any other value) of a message ).

2. contenttype: it is used to describe the MIME type of the encoding. For example, a good setting method for large JSON encoding is application/JSON.

3. replyto: The name of a common callback queue.

4. correlationid: used for an RPC request.

At the same time, we need a new class:

import com.rabbitmq.client.AMQP.BasicProperties;

1.3 correlation ID)

In the above method, we create a callback queue for each RPC request. This is very inefficient, but fortunately there is a better way. Let's create a single callback queue for each client to call.

This creates a new problem. When receiving a response that does not know which response the request belongs to in the queue, we need to set it to a unique value for each request, then, when receiving a message from a callback queue, we need to view this attribute value. On this basis, we will be able to match the response of a request. If we see an unknown correlationid value, we can safely discard these messages because they do not comply with our requirements.

You may ask, why do we discard unknown messages in the callback queue? Rather than a failure caused by an error? This is because of a possible competition on the server. Although it is unlikely, it may still happen. The RPC server will crash after giving us a big answer, however, if this happens, the RPC server will restart again to process the request, which is why repeated responses must be processed on the client.


II. Implementation

2.1. the structure is shown in:


We can see that the RPC workflow is as follows:

1. When the client starts, it creates an anonymous independent callback queue.

2. In an RPC request, a client sends a message with two features: replyto contains the callback queue to be reached and correlation_id, which is an inherent value of each request.

3. The request is sent to an rpc_queue queue.

4. the RPC server is waiting for a queue request. When a request arrives, the RPC server uses the replyto queue to send a message and return it to the client.

5. The client waits for the data in the replyto team to be called back. When a message appears, the client checks whether the correlationid value matches the value returned by the request to the application.

2.2 code implementation

Function:

private static int fib(int n) throws Exception {    if (n == 0) return 0;    if (n == 1) return 1;    return fib(n-1) + fib(n-2);}

We declare the Fibonacci function, which assumes that only valid full input (do not count on a large number, it may be the slowest Recursive Implementation). The Code of rpcserver. Java on the RPC server is as follows:

private static final String RPC_QUEUE_NAME = "rpc_queue";ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");Connection connection = factory.newConnection();Channel channel = connection.createChannel();channel.queueDeclare(RPC_QUEUE_NAME, false, false, false, null);channel.basicQos(1);QueueingConsumer consumer = new QueueingConsumer(channel);channel.basicConsume(RPC_QUEUE_NAME, false, consumer);System.out.println(" [x] Awaiting RPC requests");while (true) {    QueueingConsumer.Delivery delivery = consumer.nextDelivery();    BasicProperties props = delivery.getProperties();    BasicProperties replyProps = new BasicProperties                                     .Builder()                                     .correlationId(props.getCorrelationId())                                     .build();    String message = new String(delivery.getBody());    int n = Integer.parseInt(message);    System.out.println(" [.] fib(" + message + ")");    String response = "" + fib(n);    channel.basicPublish( "", props.getReplyTo(), replyProps, response.getBytes());    channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);}
Note:

1. establish connections, channels, and queues like all previous instances

2. Multiple server processes may be run. to spread the same load across multiple servers, we need to set the prefetchcount value through channel. basicqos.

3. Access the queue through basicconsume, and then enter the loop, waiting for the request message, processing the message, and sending the response.

The rpcclient. Java code is as follows:

private Connection connection;private Channel channel;private String requestQueueName = "rpc_queue";private String replyQueueName;private QueueingConsumer consumer;public RPCClient() throws Exception {    ConnectionFactory factory = new ConnectionFactory();    factory.setHost("localhost");    connection = factory.newConnection();    channel = connection.createChannel();    replyQueueName = channel.queueDeclare().getQueue();     consumer = new QueueingConsumer(channel);    channel.basicConsume(replyQueueName, true, consumer);}public String call(String message) throws Exception {         String response = null;    String corrId = java.util.UUID.randomUUID().toString();    BasicProperties props = new BasicProperties                                .Builder()                                .correlationId(corrId)                                .replyTo(replyQueueName)                                .build();    channel.basicPublish("", requestQueueName, props, message.getBytes());    while (true) {        QueueingConsumer.Delivery delivery = consumer.nextDelivery();        if (delivery.getProperties().getCorrelationId().equals(corrId)) {            response = new String(delivery.getBody());            break;        }    }    return response; }public void close() throws Exception {    connection.close();}
Note:

1. Establish a connection channel and declare a reply to an exclusive callback queue.

2. subscribe to the callback queue to accept the RPC response.

3. Call a method to initiate an actual RPC request.

4. Generate a unique correlationid and save it. The while loop uses this value to match the corresponding response.

5. Send a request message, which has two attribute values: relpto and correlationid.

6. Wait for a matched response.

7. The while loop does a simple job. Check whether correlationid is required for each response. If yes, save the response.

8. Return the response to the client.

Client request code:

RPCClient fibonacciRpc = new RPCClient();System.out.println(" [x] Requesting fib(30)");   String response = fibonacciRpc.call("30");System.out.println(" [.] Got '" + response + "'");fibonacciRpc.close();

2.3 complete code list

Rpcclient. Java

Package COM. xuz. RPC; import Java. util. UUID; import COM. rabbitmq. client. channel; import COM. rabbitmq. client. connection; import COM. rabbitmq. client. connectionfactory; import COM. rabbitmq. client. queueingconsumer; import COM. rabbitmq. client. amqp. basicproperties; public class rpcclient {private connection; private channel; private string requestqueuename = "rpc_queue"; private string replyqueue Name; private queueingconsumer consumer; Public rpcclient () throws exception {connectionfactory factory = new connectionfactory (); factory. sethost ("127.0.0.1"); connection = factory. newconnection (); Channel = connection. createchannel (); // response queue name. The server sends the returned information to this queue. Replyqueuename = channel. queuedeclare (). getqueue (); consumer = new queueingconsumer (Channel); channel. basicconsume (replyqueuename, true, consumer);} Public String call (string message) throws exception {string response = NULL; // each request generates a unique correlationidstring corrid = UUID. randomuuid (). tostring (); // set basic request response parameters: correlationid (UUID) and rpc_queuebasicproperties props = new basicproperties. builder (). correlationi D (corrid ). replyto (replyqueuename ). build (); system. out. println ("attributes of the client response queue: [" + props. getcorrelationid () + "," + props. getreplyto () + "]"); channel. basicpublish ("", requestqueuename, props, message. getbytes (); While (true) {queueingconsumer. delivery delivery = consumer. nextdelivery (); // If the getcorrelationid in the response queue is equal to the current corrid, the response is saved and if (delivery. getproperties (). getcorrelationid (). equals (corrid) {response = ne W string (delivery. getbody (), "UTF-8"); break;} return response;}/*** close connection * @ throws exception */Public void close () throws exception {connection. close () ;}public static void main (string [] argv) {rpcclient = NULL; string response = NULL; try {rpcclient = new rpcclient (); // call the call method to pass in the request message: Test rpcresponse = rpcclient. call ("test RPC"); system. out. println ("Response Message: [" + response + "]");} catch (exception E) {e. printstacktrace ();} finally {If (rpcclient! = NULL) {try {rpcclient. Close () ;}catch (exception ignore ){}}}}}


Rpcserver. Java:

Package COM. xuz. RPC; import COM. rabbitmq. client. channel; import COM. rabbitmq. client. connection; import COM. rabbitmq. client. connectionfactory; import COM. rabbitmq. client. queueingconsumer; import COM. rabbitmq. client. amqp. basicproperties; public class rpcserver {Private Static final string rpc_queue_name = "rpc_queue "; /*** define the function * @ Param n the input positive integer * @ return */Private Static int fib (int n) {If (n = 0) Return 0; If (n = 1) return 1; return fib (n-1) + fib (n-2);} public static void main (string [] argv) {connection = NULL; channel = NULL; try {// get the connection factory connectionfactory factory = new connectionfactory (); // set the host factory. sethost ("127.0.0.1"); // create a connection = factory. newconnection (); // create channel = connection. createchannel (); // declare the RPC queue channel. queuedeclare (rpc_queue_name, false, n Ull); // set the fair scheduling channel. basicqos (1); queueingconsumer consumer = new queueingconsumer (Channel); channel. basicconsume (rpc_queue_name, false, consumer); system. out. println ("[waiting for RPC Remote request!] "); While (true) {string response = NULL; system. Out. println (" [the server is waiting to receive messages!] "); Queueingconsumer. Delivery delivery = consumer. nextdelivery (); system. Out. println (" [the server successfully receives the message!] "); Basicproperties props = delivery. getproperties (); // obtain the reply parameter basicproperties replyprops = new basicproperties from the response queue. builder (). correlationid (props. getcorrelationid ()). build (); system. out. println ("server response queue attributes: [" + replyprops. getcorrelationid () + "]"); try {string message = new string (delivery. getbody (), "UTF-8"); response = "the server has processed the message: [" + message + "]";} catch (exception e) {system. out. println ("[.] "+ E. t Ostring (); response = "" ;}finally {// return the result to the client channel. basicpublish ("", props. getreplyto (), replyprops, response. getbytes ("UTF-8"); // sets the Message channel for confirmation. basicack (delivery. getenvelope (). getdeliverytag (), false) ;}} catch (exception e) {e. printstacktrace ();} finally {If (connection! = NULL) {try {connection. Close () ;}catch (exception ignore ){}}}}}
2.4 RPC Testing

1. Run rpcclient to send a response request:

2. Run prcserver to receive response requests:

Download source code:

Rabbitmq RPC source code

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.