RabbitMQ的七种工作模式-RPC模式(六)

6.RPC
在这里插入图片描述

RPC工作原理:

1.客户端发起RPC请求时,request请求中会发送两个参数replyTo和correlationId

replyTo:同步互斥队列,也就是该请求对应的队列

correlationId:唯一标识

2.请求存入rpc队列,采用的是有界数组阻塞队列(ArrayBlockingQueue)

3.消息接受端(也就是服务器端)接受到请求之后,利用replyTo中的携带的数据,处理任务并返回结果,返回结果中携带correlationId和具体结果

咱们来复习一下BlockingQueue(ArrayBlockingQueue回头再单独学习):

阻塞队列:在队列元素为空的情况下,阻塞获取数据,直到队列不空;在队列元素已满的情况下,阻塞插入数据,直到队列不满

    boolean add(E e);     // 将数据插入队列
    boolean offer(E e);   // 将数据插入队列,比add好,因为add队列满时抛出异常,offer是返回false;                             // 根本是add = {!offer(e) throw new IllegalStateException("Deque full");}
    void put(E e) throws InterruptedException; // 等到队列不满时插入元素
    boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException; // 如果队列满时,等待一段时间,待超时后返回false;如果超时时间内不满,就插入返回true
    E take() throws InterruptedException;  // 从队列头部取出数据(即获取到数据并将其从队列中删除)
    E poll(long timeout, TimeUnit unit) throws InterruptedException; // 在超时时间内取出队头数据
    int remainingCapacity();   // 队列剩余容量
    boolean remove(Object o);   // 将元素从队列中删除
    public boolean contains(Object o); // 判断元素是否存在队列中
    int drainTo(Collection<? super E> c); // 将队列数据迁移到另一个集合中
    int drainTo(Collection<? super E> c, int maxElements); // 将队列数据迁移到另一个集合中,限定大小

此处做一点小优化,既然服务每次都需要去做连接,不如把Connection提取出来:

    import com.rabbitmq.client.Connection;
    import com.rabbitmq.client.ConnectionFactory;
    
    /**
     * @author Becolette
     * @description AMQP连接公共服务
     * @datetime 2020/9/24 15:02
     */
    
    public class MqConnection {
    
        /**
         * 创建RabbitMq连接
         *
         * @param hostName
         * @param port
         * @param userName
         * @param password
         * @param vHost
         * @return
         */
        public static Connection createConnection(String hostName, int port, String userName,
                                                  String password, String vHost) {
            ConnectionFactory factory = new ConnectionFactory();
            factory.setHost(hostName);
            factory.setPort(port);
            factory.setUsername(userName);
            factory.setPassword(password);
            factory.setVirtualHost(vHost);
    
            Connection connection = null;
            try {
                connection = factory.newConnection();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return connection;
        }
    
    }

客户端

    import com.becolette.amqp.rabbit.simple.MqConnection;
    import com.rabbitmq.client.AMQP;
    import com.rabbitmq.client.Channel;
    import com.rabbitmq.client.Connection;
    
    import java.io.IOException;
    import java.util.UUID;
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.BlockingQueue;
    
    /**
     * @author Becolette
     * @description 客户端
     * @datetime 2020/9/28 16:08
     */
    
    public class RpcClient{
    
        /**
         * 1.客户端发起RPC请求时,request请求中会发送两个参数replyTo和correlationId
         * replyTo:同步互斥队列,也就是该请求对应的队列
         * correlationId:唯一标识
         * 2.请求存入rpc队列,采用的是有界数组阻塞队列(ArrayBlockingQueue)
         * 3.消息接受端(也就是服务器端)接受到请求之后,利用replyTo中的携带的数据,处理任务并返回结果,返回结果中携带correlationId和具体结果
         */
        private static String requestQueueName = "rpc_queue";
    
        public static void main(String[] argv) throws Exception {
            // 第一步:创建连接和通道
            Connection connection = null;
            Channel channel = null;
            try {
                connection = MqConnection.createConnection("localhost", 5672, "guest", "guest", "/");
                channel = connection.createChannel();
    
                // 第二步:发起请求
                for (int i = 0; i < 32; i++) {
                    String i_str = Integer.toString(i);
                    System.out.println(" [x] Requesting fib(" + i_str + ")");
                    String response = call(i_str, channel);
                    System.out.println(" [.] Got '" + response + "'");
                }
            }catch (Exception e){
                e.printStackTrace();
            } finally {
                channel.close();
                connection.close();
            }
        }
    
        public static String call(String message, Channel channel) throws IOException, InterruptedException {
            // 使用uuid作为唯一键
            final String corrId = UUID.randomUUID().toString();
    
            String replyQueueName = channel.queueDeclare().getQueue();
            // 将correlationId和replyTo作为属性参数发送给服务器
            AMQP.BasicProperties props = new AMQP.BasicProperties
                    .Builder()
                    .correlationId(corrId)
                    .replyTo(replyQueueName)
                    .build();
    
            /** 采用的是工作模式 **/
            channel.basicPublish("", requestQueueName, props, message.getBytes("UTF-8"));
    
            /** 有界阻塞队列大小为1 */
            final BlockingQueue<String> response = new ArrayBlockingQueue<>(1);
    
            String cTag = channel.basicConsume(replyQueueName, true, (consumerTag, delivery) -> {
                if (delivery.getProperties().getCorrelationId().equals(corrId)) {
                    // 入队
                    response.offer(new String(delivery.getBody(), "UTF-8"));
                }
            }, consumerTag -> {
            });
    
            String result = response.take();
            channel.basicCancel(cTag);
            return result;
        }
    
    }

服务端

    import com.becolette.amqp.rabbit.simple.MqConnection;
    import com.rabbitmq.client.*;
    
    /**
     * @author Becolette
     * @description 服务端
     * @datetime 2020/9/28 16:10
     */
    public class RpcServer {
    
        private static final String RPC_QUEUE_NAME = "rpc_queue";
    
        /**
         * 斐波那契数列
         *
         * @param n
         * @return
         */
        private static int fib(int n) {
            if (n == 0 || n == 1) {
                return n;
            }
            return fib(n - 1) + fib(n - 2);
        }
    
        public static void main(String[] argv) throws Exception {
            Connection connection = MqConnection.createConnection("localhost", 5672, "guest", "guest", "/");
    
            try (Channel channel = connection.createChannel()) {
                // 声明队列,队列名,不进行持久化,不互斥,不自动删除,无其他参数
                channel.queueDeclare(RPC_QUEUE_NAME, false, false, false, null);
                // 清除给定队列的内容
                channel.queuePurge(RPC_QUEUE_NAME);
                // 公平分配
                channel.basicQos(1);
    
                System.out.println(" [x] Awaiting RPC requests");
                Object monitor = new Object();
                // 返回给客户端参数
                DeliverCallback deliverCallback = (consumerTag, delivery) -> {
                    AMQP.BasicProperties replyProps = new AMQP.BasicProperties
                            .Builder()
                            .correlationId(delivery.getProperties().getCorrelationId())
                            .build();
    
                    String response = "";
                    try {
                        String message = new String(delivery.getBody(), "UTF-8");
                        int n = Integer.parseInt(message);
    
                        System.out.println(" [.] fib(" + message + ")");
                        response += fib(n);
                    } catch (RuntimeException e) {
                        System.out.println(" [.] " + e.toString());
                    } finally {
                        channel.basicPublish("", delivery.getProperties().getReplyTo(), replyProps, response.getBytes("UTF-8"));
                        channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
                        // RabbitMq consumer worker thread notifies the RPC server owner thread
                        synchronized (monitor) {
                            monitor.notify();
                        }
                    }
                };
    
                channel.basicConsume(RPC_QUEUE_NAME, false, deliverCallback, (consumerTag -> {}));
                
                // 等到所有的请求处理完成后,再把数据返回给客户端
                while (true) {
                    synchronized (monitor) {
                        try {
                            monitor.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

RPC工作优缺点

优点:

1.客户端请求可以发送给多个消费者,取最快返回结果

2.客户端发送一条请求,结果无需同步返回

3.无需关心有没有服务器处理请求,响应超时、出错或故障该如何处理;无需关心无效输入信息

缺点:

1.RPC的响应可能会很慢

2.出现错误时,排查原因比较麻烦

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值