RabbitMQ Learning Series (5): RPC Remote Procedure Call

I have talked about the installation and usage of RabbitMQ, and how RabbitMQ is used in general business scenarios. If you don't know, you can read my previous blog, http://www.cnblogs.com/zhangweizhong/category/855479.html

However, a friend recently asked me what RabbitMQ RPC does and what is its use.

In fact, RabbitMQ RPC realizes the function of rpc through message queue (Message Queue), that is, the client sends a defined Queue message to the server, and the message carried in it should be the parameters of the method to be called by the server, and use Propertis tells the server to return the result to the specified Queue .

1. Features of RabbitMQ RPC

  • Message Queue stores all request messages, then processes them, and decouples them from the client.
  • Message Queue introduces new nodes, and the reliability of the system will be affected by Message Queue nodes.
  • Message Queue is an asynchronous one-way message. Sending messages is designed not to wait for message processing to complete.

So for synchronous return requirements, Message Queue is a good direction.

2. Characteristics of ordinary PRC

  • Synchronous calls, RPC is a very natural and intuitive way to use in scenarios where you want to wait for a result to be returned/processed. Of course RPC can also be an asynchronous call.
  • The client has thread consumption due to waiting for the result.

If used in an asynchronous RPC way, the client thread consumption can be removed. However, the message request cannot be temporarily stored like a message, and the pressure will be directly transmitted to the server.

3. Description of applicable occasions

  • RPC is suitable when you want to get results synchronously.
  • If you want to use it easily, use RPC; RPC operations are based on interfaces, which are easy to use and simulate local calls. Asynchronous programming is more complicated.
  • If you don't want the client to be limited by the speed of the server, you can use Message Queue.

4.RabbitMQ RPC workflow:

 

basic concept:

Callback queue Callback queue , the client sends a request to the server, and after the server processes the request, the processing result is stored in a storage bank. In order to obtain the processing result, the client sends a callback queue address reply_to at the same time when sending a request to the server.

Correlation id is a correlation identifier . The client may send multiple requests to the server. When the server finishes processing, the client cannot identify the response in the callback queue and the request. In order to deal with this situation, the client sends each request with a unique correlation_id attribute, so that the client can distinguish which request this response belongs to based on the value of the correlation_id field in the callback queue.

Process description :

  • When the client starts up, it creates an anonymous and exclusive callback queue.
  • In an RPC request, the client sends a message with two properties: a reply_to property that sets the callback queue, and a correlation_id property that sets a unique value.
  • Send the request to an rpc_queue queue.
  • The server waits for requests to be sent to this queue. When the request comes, it performs its work and sends a message with the result of the execution to the queue specified by the reply_to field.
  • The client waits for data in the callback queue. When a message appears, it checks the correlation_id property. If the value of this property matches the request, return it to the application

 5. Complete code:

  1. Create two console programs as RPC Server and RPC Client, refer to RabbitMQ.Client,

  2. RPC Server

copy code
    class Program
    {
        static void Main(string[] args)
        {
            var factory = new ConnectionFactory() { HostName = "localhost", VirtualHost = "OrderQueue", UserName = "zhangweizhong", Password = "weizhong1988", Port = 5672 };
            using (var connection = factory.CreateConnection())
            using (var channel = connection.CreateModel())
            {
                channel.QueueDeclare(queue: "rpc_queue",
                                     durable: false,
                                     exclusive: false,
                                     autoDelete: false,
                                     arguments: null);
                channel.BasicQos(0, 1, false);
                var consumer = new QueueingBasicConsumer(channel);
                channel.BasicConsume(queue: "rpc_queue",
                                     noAck: false,
                                     consumer: consumer);
                Console.WriteLine(" [x] Awaiting RPC requests");

                while (true)
                {
                    string response = null;
                    var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();

                    var body = ea.Body;
                    var props = ea.BasicProperties;
                    var replyProps = channel.CreateBasicProperties ();
                    replyProps.CorrelationId = props.CorrelationId;

                    try
                    {
                        var message = Encoding.UTF8.GetString(body);
                        int n = int.Parse(message);
                        Console.WriteLine(" [.] fib({0})", message);
                        response = fib(n).ToString();
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(" [.] " + e.Message);
                        response = "";
                    }
                    finally
                    {
                        var responseBytes = Encoding.UTF8.GetBytes(response);
                        channel.BasicPublish(exchange: "",
                                             routingKey: props.ReplyTo,
                                             basicProperties: replyProps,
                                             body: responseBytes);
                        channel.BasicAck(deliveryTag: ea.DeliveryTag,
                                         multiple: false);
                    }
                }
            }
        }

        /// <summary>
        /// Assumes only valid positive integer input.
        /// Don't expect this one to work for big numbers,
        /// and it's probably the slowest recursive implementation possible.
        /// </summary>
        private static int fib(int n)
        {
            if (n == 0 || n == 1)
            {
                return n;
            }

            Thread.Sleep(1000 * 10);

            return n;
        }
    }
copy code

 

  3. RPC Client

copy code
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 10; i++)
            {
                Stopwatch watch = new Stopwatch();

                watch.Start();

                var rpcClient = new RPCClient();

                Console.WriteLine(string.Format(" [x] Requesting fib({0})", i));

                var response = rpcClient.Call(i.ToString());

                Console.WriteLine(" [.] Got '{0}'", response);

                rpcClient.Close();

                watch.Stop();

                Console.WriteLine(string.Format(" [x] Requesting complete {0} ,cost {1} ms", i, watch.Elapsed.TotalMilliseconds));
            }

            Console.WriteLine(" complete!!!! ");


            Console.ReadLine();
        }
    }

    class RPCClient
    {
        private IConnection connection;
        private IModel channel;
        private string replyQueueName;
        private QueueingBasicConsumer consumer;

        public RPCClient()
        {
            var factory = new ConnectionFactory() { HostName = "localhost", VirtualHost = "OrderQueue", UserName = "zhangweizhong", Password = "weizhong1988", Port = 5672 };
            connection = factory.CreateConnection();
            channel = connection.CreateModel();
            replyQueueName = channel.QueueDeclare().QueueName;
            consumer = new QueueingBasicConsumer(channel);
            channel.BasicConsume(queue: replyQueueName,
                                 noAck: true,
                                 consumer: consumer);
        }

        public string Call(string message)
        {
            var corrId = Guid.NewGuid().ToString();
            var props = channel.CreateBasicProperties();
            props.ReplyTo = replyQueueName;
            props.CorrelationId = corrId;

            var messageBytes = Encoding.UTF8.GetBytes(message);
            channel.BasicPublish(exchange: "",
                                 routingKey: "rpc_queue",
                                 basicProperties: props,
                                 body: messageBytes);

            while (true)
            {
                var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();
                if (ea.BasicProperties.CorrelationId == corrId)
                {
                    return Encoding.UTF8.GetString(ea.Body);
                }
            }
        }

        public void Close()
        {
            connection.Close();
        }
    }
copy code

  4. Run Server and Client respectively

 

6. Finally

  1. Refer to the RPC of RabbitMQ official tutorial, address: http://www.rabbitmq.com/tutorials/tutorial-six-dotnet.html

  2. Download the source code of this article, http://files.cnblogs.com/files/zhangweizhong/Weiz.RabbitMQ.RPC.rar

  3. Original blog address: http://fpeach.com/post/2016/12/01/RabbitMQ%E5%AD%A6%E4%B9%A0%E7%B3%BB%E5%88%97%EF% BC%88%E4%BA%94%EF%BC%89-RPC-%E8%BF%9C%E7%A8%8B%E8%BF%87%E7%A8%8B%E8%B0%83%E7 %94%A8.aspx

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325994291&siteId=291194637