RabbitMq 遠程過程調用RPC(七)

RPC:
    遠程過程調用(RPC): 客戶端發送一個請求到遠程服務器上,遠程服務器接收請求並處理結果,將結果響應給客戶端,這個過程被稱爲遠程過程調用。

RPC涉及到的基本知識:
(1)關於隊列:整個過程會設計到兩個隊列一個是專門保存請求的隊列,一般名字被稱爲rpc_queue,另一個隊列被稱爲響應隊列,專門用於保存服務器處理的響應結果,這個隊列的名字是隨機生成的字符串。

(2)關於消息的基本屬性BasicProperties:回覆(replyTo):是響應隊列的名字,當服務器接收請求並處理好結果,服務器需要知道將響應的信息發送到哪個隊列中;關聯id(correlationId):是一個UUID值,發消息的時候會帶上這個值,該值在客戶端接收響應時用於判斷接收到的響應消息是否是自己發出請求對應的響應; 客戶端在發送請求時需要帶上replyTo和correlationId兩個屬性。

(3)其他屬性:contentType:內容類型,用來描述編碼的MIME類型。例如,經常使用JSON編碼是將此屬性設置爲一個很好的做法:application/json,在rpc中該屬性不是必須的。

RPC的過程描述:
    客戶端首先將請求消息發送到請求隊列,在發送請求時需要指定replyTo和correlationId兩個值。
    服務端需要預先訂閱請求隊列(rpc_queue),以便服務器端能隨時接受到請求消息,當服務端接收到請求消息時對請求進行處理,將處理結果發送到響應隊列(隨機隊列)中。
    客戶端還要預先訂閱響應隊列(隨機隊列),以便當服務器發送響應消息到響應隊列中,客戶端能及時收到響應結果,服務器在將響應發送到響應隊列中還要指定correlationId值(類似於唯一標記當前的請求),這樣當客戶端從響應隊列中接收到消息時就可以通過correlationId的值是否和發送請求的關聯id值是否相同,如果相同就證明這個響應結果就是這個請求對應的響應結果。注意這個預先訂閱響應隊列的步驟需要在客戶端中完成,最好在客戶端發送請求消息前就完成。

在這裏插入圖片描述

客戶端OneQueueTwoKeyProducer

package com.richfit.richfit.controller.RabbitMq.msgprodutreceiver4;

import com.rabbitmq.client.*;
import com.richfit.richfit.RabbitConnectionUtil;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.UUID;

/**
 * @ClassName OneQueueTwoKeyProducer
 * @Description: 遠程過程調用(RPC): 客戶端發送一個請求到遠程服務器上,遠程服務器接收請求並處理結果,將結果響應給客戶端,這個過程被稱爲遠程過程調用。
 *
 * RPC涉及到的基本知識:
 * 關於隊列:整個過程會設計到兩個隊列一個是專門保存請求的隊列,一般名字被稱爲rpc_queue,另一個隊列被稱爲響應隊列,專門用於保存服務器處理的響應結果,這個隊列的名字是隨機生成的字符串。
 * 關於消息的基本屬性BasicProperties:回覆(replyTo):是響應隊列的名字,當服務器接收請求並處理好結果,服務器需要知道將響應的信息發送到哪個隊列中;關聯id(correlationId):是一個UUID值,發消息的時候會帶上這個值,該值在客戶端接收響應時用於判斷接收到的響應消息是否是自己發出請求對應的響應; 客戶端在發送請求時需要帶上replyTo和correlationId兩個屬性。
 * 其他屬性:contentType:內容類型,用來描述編碼的MIME類型。例如,經常使用JSON編碼是將此屬性設置爲一個很好的做法:application/json,在rpc中該屬性不是必須的


RPC的過程描述:
客戶端首先將請求消息發送到請求隊列,在發送請求時需要指定replyTo和correlationId兩個值;
服務端需要預先訂閱請求隊列(rpc_queue),以便服務器端能隨時接受到請求消息,當服務端接收到請求消息時對請求進行處理,將處理結果發送到響應隊列(隨機隊列)中
客戶端還要預先訂閱響應隊列(隨機隊列),以便當服務器發送響應消息到響應隊列中,客戶端能及時收到響應結果,服務器在將響應發送到響應隊列中還要指定correlationId值(類似於唯一標記當前的請求),這樣當客戶端從響應隊列中接收到消息時就可以通過correlationId的值是否和發送請求的關聯id值是否相同,如果相同就證明這個響應結果就是這個請求對應的響應結果。注意這個預先訂閱響應隊列的步驟需要在客戶端中完成,最好在客戶端發送請求消息前就完成。

 * @Author BruthLi
 * @Date 2020/2/14
 * @Version V1.0
 **/
@Component
public class OneQueueTwoKeyProducer {
    public static void main(String[] args) throws Exception{
        //1.獲取連接
        Connection connection = RabbitConnectionUtil.getConnection("localhost", 5672, "/", "guest", "guest");
        //2.聲明通道
        Channel channel = connection.createChannel();
        // 預先定義響應的結果,即預先訂閱響應結果的隊列,先訂閱響應隊列,再發送消息到請求隊列
        String reyply_to_queue = channel.queueDeclare().getQueue();
        final String correlationId = UUID.randomUUID().toString();
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                if (properties.getCorrelationId().equals(correlationId)) {
                    String message = new String(body, "UTF-8");
                    System.out.println("已接收到服務器的響應結果:" + message);
                }
            }
        };
        channel.basicConsume(reyply_to_queue, true, consumer);


        // 將消息發送到請求隊列中
        String rpc_queuqu = "rpc_queue";
        String message = "Hello RabbitMQ";
        AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder().correlationId(correlationId).replyTo(reyply_to_queue).build();
        channel.basicPublish("", rpc_queuqu, properties, message.getBytes("UTF-8"));
        System.out.println("已發出請求請求消息:" + message);
        Thread.sleep(100000);
    }
}

服務端OneQueueTwoKeyReceiver1

package com.richfit.richfit.controller.RabbitMq.msgprodutreceiver4;

import com.rabbitmq.client.*;
import com.richfit.richfit.RabbitConnectionUtil;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * @ClassName OneQueueTwoKeyReceiver1
 * @Description:
 * @Author BruthLi
 * @Date 2020/2/14
 * @Version V1.0
 **/
@Component
public class OneQueueTwoKeyReceiver1 {
    public static void main(String[] args) throws Exception{
        //1.獲取連接
        Connection connection = RabbitConnectionUtil.getConnection("localhost", 5672, "/", "guest", "guest");
        //2.聲明通道
        Channel channel = connection.createChannel();
        String rpc_queuqu = "rpc_queue";
        channel.queueDeclare(rpc_queuqu, false, false, false, null);
        channel.basicQos(1);
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println("服務端:已接收到請求消息:" + message);
                // 服務器端接收到消息並處理消息
                String response = "{'code': 200, 'data': '" + message+ "'}";
                // 將消息發佈到reply_to響應隊列中
                AMQP.BasicProperties replyProperties = new AMQP.BasicProperties.Builder().
                        correlationId(properties.getCorrelationId()).build();
                String replyTo = properties.getReplyTo();
                channel.basicPublish("", replyTo, replyProperties, response.getBytes("UTF-8"));
                System.out.println("服務端:請求已處理完畢,響應結果" + response + "已發送到響應隊列中");
                // 手動應答
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        // 手動應答模式
        channel.basicConsume(rpc_queuqu, false, consumer);
        System.out.println("服務端:已訂閱請求隊列(rpc_queue), 開始等待接收請求消息...");
        Thread.sleep(100000);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章