RabbitMQ 小记

RabbitMQ中的交换机类型

  • Direct
  • Fanout
  • Topic
  • headers

Direct Exchange(直连交换机)

直连交换机的特点是消息队列通过routingKey与交换机进行绑定,相同的routingKey会获得相同的消息。一个队列可以通过多个不同的routingKey与交换机进行绑定。不同的队列也可以通过相同的routingKey绑定交换机。

Fanout Exchange(扇出交换机)

扇出交换机的特点是类似于广播,只要队列与该类型的交换机绑定,所有发送到该交换机的信息都会被转发到所有与之绑定的队列,与routingKey无关。

Topic Exchange(主题交换机)

应用范围最广的交换机类型,消息队列通过消息主题与交换机绑定。一个队列可以通过多个主题与交换机绑定,多个消息队列也可以通过相同消息主题和交换机绑定。并且可以通过通配符(*或者#)进行多个消息主题的适配。
消息主题的一般格式为xxx.xxx.xxx(x为英文字母,每个单词用英文句号隔开)。*通配符可以适配一个单词,#可以适配零个或者多个单词。
通配符适配如下:
*.xxx.#。此主题可以适配xxx前面只有一个单词后面有零个或者多个单词的所有消息主题。

* 可以匹配一个单词
# 可以匹配多个

如  路由键 a.b.c.d   	
 a.*.c.*  可以匹配
 a.#       可以匹配

Header Exchenge(头交换机)

与routingKey无关,匹配机制是匹配消息头中的属性信息。在绑定消息队列与交换机之前声明一个map键值对,通过这个map对象实现消息队列和交换机的绑定。当消息发送到RabbitMQ时会取到该消息的headers与Exchange绑定时指定的键值对进行匹配;如果完全匹配则消息会路由到该队列,否则不会路由到该队列。
匹配规则x-match有下列两种类型:
x-match = all :表示所有的键值对都匹配才能接受到消息
x-match = any :表示只要有键值对匹配就能接受到消息

常用模式

  • 简单模式
    • 在这里插入图片描述

    • 最简单的模式,就1个消费者负责处理信息

  • 工作模式
    • 在这里插入图片描述

    • 最常用的模式,多个消费者,同时处理,可以更加合理的提升处理速度

  • 广播模式
    • 在这里插入图片描述

    • 同一条消息,会被推送到多个与之绑定的队列,通常用于群发消息

  • 路由模式
    • 在这里插入图片描述

    • 通常用于订阅数据,错误/异常通知,可以根据路由键来过滤,只获取自己需要的内容

  • 主题模式
    • 在这里插入图片描述

    • 类似于路由模式,只是路由键支持通配符

各种模式,无非就是交换机类型与路由键,队列参数之前的各种设置,根据实际业务需求,来合理规划,可以更好的使用!

各种模式的配置规则

在这里插入图片描述

参考代码(使用原生API来演示,以更好理解底层运作):

依赖

<dependency>
  <groupId>com.rabbitmq</groupId>
  <artifactId>amqp-client</artifactId>
  <version>5.4.3</version>
</dependency>

工具类

package com.example.rabbitmq;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @version 0.0.1
 * @Author: LianZuoYang
 * @Create: 2022-08-27 10:28
 */
public class Utils {
    public static Channel getChannel() throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.64.140");//MQ的服务器地址
        factory.setPort(5672);//MQ的端口
        factory.setUsername("guest");
        factory.setPassword("guest");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();//通信通道
        return channel;
    }
}

简单模式

生产者

package com.example.rabbitmq.m1;

import com.example.rabbitmq.Utils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.Scanner;
import java.util.concurrent.TimeoutException;

/**
 * 简单模式,1个收,1个发
 * @version 0.0.1
 * @Author: LianZuoYang
 * @Create: 2022-08-27 09:49
 */
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        Scanner scanner = new Scanner(System.in);


        String queueName = "HelloWord";
        //连接RabbitMQ
        Channel channel = Utils.getChannel();//通信通道
        //创建队列(如已存在则不创建)

        //持久化:会将队列信息保存到硬盘,非持久化:只在内存里保存队列信息
        //独占:只能被一个消费者消费,非独占:可以被多个消费者共享
        //自动删除:没有消费者时,服务器自动删除队列,不自动删除:没有消费者时,服务器不自动删除队列
        //其他参数:一些队列的其他可选参数

        //参数列表            队列名       是否持久化     是否独占         是否自动删除       其他参数
        channel.queueDeclare(queueName, false, false, false, null);
        //向队列发送数据
        byte[] bytes = "test message".getBytes();


        //参数列表        交换机(空串是默认)   队列名   消息的其他参数    消息内容
        //channel.basicPublish("", queueName, null, bytes);

        while (true){
            String s = scanner.nextLine();
            channel.basicPublish("", queueName, null, s.getBytes());
        }
    }
}

消费者

package com.example.rabbitmq.m1;

import com.example.rabbitmq.Utils;
import com.rabbitmq.client.CancelCallback;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
import com.rabbitmq.client.Delivery;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 简单模式,1个收,1个发
 * @version 0.0.1
 * @Author: LianZuoYang
 * @Create: 2022-08-27 10:29
 */
public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        String queueName = "HelloWord";
        //连接RabbitMQ
        Channel channel = Utils.getChannel();//通信通道

        //消费者也创建一下队列,可以让消费者跟生产者的启动顺序灵活变动,谁先启动,谁创建
        //创建队列(如已存在则不创建)
        channel.queueDeclare(queueName, false, false, false, null);

        //处理消息的回调对象
        DeliverCallback deliverCallback = new DeliverCallback() {
            @Override
            public void handle(String consumerTag, Delivery message) throws IOException {
                byte[] body = message.getBody();
                String msg = new String(body);
                System.out.println("收到消息: " + msg);
            }
        };
        //消费者取消接收消息的回调对象(不再从队列接收消息的回调)
        CancelCallback cancelCallback = new CancelCallback() {
            @Override
            public void handle(String consumerTag) throws IOException {

            }
        };

        //从队列持续的接收消息,消息需要传递到一个回调对象进行处理
        //参数true为自动确认,服务端发送消息给消费者以后,就删除了服务端的消息.
        //推荐使用false手动确认,需要消费者处理完消息后,手动发送确认消息,让服务端删除消息,这样不会造成消息丢失
        channel.basicConsume(queueName, true, deliverCallback, cancelCallback);


    }
}

工作者模式

生产者

package com.example.rabbitmq.m2;

import com.example.rabbitmq.Utils;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.MessageProperties;

import java.io.IOException;
import java.util.Scanner;
import java.util.concurrent.TimeoutException;

/**
 * 工作模式,多个收,1个发
 * @version 0.0.1
 * @Author: LianZuoYang
 * @Create: 2022-08-27 10:55
 */
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        Scanner scanner = new Scanner(System.in);

        String queueName = "task_queue";
        //连接RabbitMQ
        Channel channel = Utils.getChannel();//通信通道
        //创建队列(如已存在则不创建)

        //持久化:会将队列信息保存到硬盘,非持久化:只在内存里保存队列信息
        //独占:只能被一个消费者消费,非独占:可以被多个消费者共享
        //自动删除:没有消费者时,服务器自动删除队列,不自动删除:没有消费者时,服务器不自动删除队列
        //其他参数:一些队列的其他可选参数

        //参数列表            队列名       是否持久化     是否独占         是否自动删除       其他参数
        channel.queueDeclare(queueName, true, false, false, null);
        //向队列发送数据
        byte[] bytes = "test message".getBytes();


        //参数列表        交换机(空串是默认)   队列名   消息的其他参数    消息内容
        //channel.basicPublish("", queueName, null, bytes);


        //持久化的消息
        AMQP.BasicProperties persistentBasic = MessageProperties.PERSISTENT_BASIC;
        while (true){
            String s = scanner.nextLine();
            channel.basicPublish("", queueName, persistentBasic, s.getBytes());
        }
    }
}

消费者

package com.example.rabbitmq.m2;

import com.example.rabbitmq.Utils;
import com.rabbitmq.client.CancelCallback;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
import com.rabbitmq.client.Delivery;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 工作模式,多个收,1个发
 *
 * @version 0.0.1
 * @Author: LianZuoYang
 * @Create: 2022-08-27 10:29
 */
public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        //启动2个消费者.
        //此模式是轮询的,按照顺序发送,可能造成某个消费者繁忙的时候,还会分配消息给他
        //比如有消息1-10,消费者a,b
        //那么a会处理1,3,5,7,9
        //b会处理2,4,6,8,10
        for (int i = 0; i < 2; i++) {
            createConsumer();
        }
    }

    private static void createConsumer() throws IOException, TimeoutException {
        String queueName = "task_queue";
        Channel channel = Utils.getChannel();//通信通道
        channel.queueDeclare(queueName, false, false, false, null);
        //处理消息的回调对象
        DeliverCallback deliverCallback = new DeliverCallback() {
            @Override
            public void handle(String consumerTag, Delivery message) throws IOException {
                byte[] body = message.getBody();
                String msg = new String(body);
                for (int j = 0; j < msg.length(); j++) {
                    if (msg.charAt(j) == '.') {//碰到.就等待1秒
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
                System.out.println(consumerTag + " >> 处理完毕:" + msg);
                //因为basicConsume里设置了autoAck为false,需要手动发送确认回执
                //获取消息的编号
                long deliveryTag = message.getEnvelope().getDeliveryTag();
                //处理完成,发送消息处理完毕的回执
                channel.basicAck(deliveryTag,false);
            }
        };
        //消费者取消接收消息的回调对象(不再从队列接收消息的回调)
        CancelCallback cancelCallback = new CancelCallback() {
            @Override
            public void handle(String consumerTag) throws IOException {

            }
        };

        //设置Qos为1,既一次只收1条消息,在消息处理完之前,不接收其他消息,保证消息的合理分配
        channel.basicQos(1);

        //从队列持续的接收消息,消息需要传递到一个回调对象进行处理
        //参数true为自动确认,服务端发送消息给消费者以后,就删除了服务端的消息.
        //推荐使用false手动确认,需要消费者处理完消息后,手动发送确认消息,让服务端删除消息,这样不会造成消息丢失
        channel.basicConsume(queueName, false, deliverCallback, cancelCallback);
    }
}

广播/群发 模式

生产者

package com.example.rabbitmq.fanout;

import com.example.rabbitmq.Utils;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;

import java.io.IOException;
import java.util.Scanner;
import java.util.concurrent.TimeoutException;

/**
 * 广播/群发模式,交换机类型使用Fanout
 * 生产者只需要往交换机内投递消息,每个绑定了此交换机的消费者都会收到消息
 * @version 0.0.1
 * @Author: LianZuoYang
 * @Create: 2022-08-27 10:55
 */
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        Scanner scanner = new Scanner(System.in);
        //自定义一个交换机名称(需要跟消费者那边一致)
        String exchangeName = "logs";
        //连接RabbitMQ
        Channel channel = Utils.getChannel();//通信通道
        //创建交换机 交换机类型为Fanout,此交换机是广播模式的,即所有绑定了此交换机的队列都会收到消息
        channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT);


        while (true) {
            System.out.print("输入消息:");
            String s = scanner.nextLine();
            //向交换机发送数据
            channel.basicPublish(exchangeName, "", null, s.getBytes());
        }
    }
}

消费者

package com.example.rabbitmq.fanout;

import com.example.rabbitmq.Utils;
import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 广播/群发模式
 * 消费者需要自己创建队列,绑定到指定的Fanout类型交换机上,即可收到消息
 * @version 0.0.1
 * @Author: LianZuoYang
 * @Create: 2022-08-27 10:29
 */
public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        //启动3个消费者.
        for (int i = 1; i < 4; i++) {
            createConsumer(i);
        }
    }

    private static void createConsumer(int index) throws IOException, TimeoutException {
        //自定义一个交换机名称(需要跟生产者那边一致)
        String exchangeName = "logs";

        String queueName = "queue_" + index; //在此模式下,队列名应当使用随机名称,此处为了方便观察,用index生成
        Channel channel = Utils.getChannel();//通信通道
        //创建交换机(可能消费者先启动了,那么就由消费者创建交换机)
        channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT);

        //创建自己的队列
        //                                  非持久化,       独占,         自动删除
        channel.queueDeclare(queueName, false, true, true, null);

        //将队列绑定到绑定到logs交换机上
        //第三个参数对Fanout模式无效
        channel.queueBind(queueName, exchangeName, "");


        //处理消息的回调对象
        DeliverCallback deliverCallback = new DeliverCallback() {
            @Override
            public void handle(String consumerTag, Delivery message) throws IOException {
                byte[] body = message.getBody();
                String msg = new String(body);
                System.out.println(queueName + " >> 收到消息:" + msg);
            }
        };
        //消费者取消接收消息的回调对象(不再从队列接收消息的回调)
        CancelCallback cancelCallback = new CancelCallback() {
            @Override
            public void handle(String consumerTag) throws IOException {

            }
        };
        //接收消息
        channel.basicConsume(queueName, true, deliverCallback, cancelCallback);
        System.out.println(queueName + " >> 启动");
    }
}

路由模式

生产者

package com.example.rabbitmq.direct;

import com.example.rabbitmq.Utils;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;

import java.io.IOException;
import java.util.Scanner;
import java.util.concurrent.TimeoutException;

/**
 * 路由模式,交换机类型使用direct
 * 生产者只需要往交换机内投递消息,每个绑定了此交换机的消费者跟自动根据路由键的类型来收取指定消息
 *
 * @version 0.0.1
 * @Author: LianZuoYang
 * @Create: 2022-08-27 10:55
 */
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        Scanner scanner = new Scanner(System.in);
        //自定义一个交换机名称(需要跟消费者那边一致)
        String exchangeName = "direct_logs";
        //连接RabbitMQ
        Channel channel = Utils.getChannel();//通信通道
        //创建交换机 交换机类型为Fanout,此交换机是广播模式的,即所有绑定了此交换机的队列都会收到消息
        channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT);

        while (true) {

            System.out.print("输入路由键(1/2/3分别对应info/warning/error):");
            String k = scanner.nextLine();
            switch (k) {
                case "1":
                    k = "info";
                    break;
                case "2":
                    k = "warning";
                    break;
                default:
                    k = "error";
            }

            System.out.print("输入消息:");
            String s = scanner.nextLine();
            //向交换机发送数据,此时需要带上 路由键 用以区分消息类型,才能分发到不同的队列
            System.out.println("发送 " + k + " >> " + s);
            channel.basicPublish(exchangeName, k, null, s.getBytes());
            System.out.println("-----------------------------------------------");
        }
    }
}

消费者

package com.example.rabbitmq.direct;

import com.example.rabbitmq.Utils;
import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 路由模式
 * 消费者需要自己创建队列,绑定到指定的direct类型交换机上,并指定路由键,来收取该键的消息
 * @version 0.0.1
 * @Author: LianZuoYang
 * @Create: 2022-08-27 10:29
 */
public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        //启动消费者.分别订阅不同的路由键
        //最后一个消费者,同时订阅warning跟error
        createConsumer("info");
        createConsumer("warning");
        createConsumer("error");
        createConsumer("warning,error");

    }

    private static void createConsumer(String key) throws IOException, TimeoutException {
        //自定义一个交换机名称(需要跟生产者那边一致)
        String exchangeName = "direct_logs";

        //在此模式下,队列名应当使用随机名称,此处为了方便观察,用index生成
        String queueName = "queue_" + key;

        Channel channel = Utils.getChannel();//通信通道

        //创建交换机(可能消费者先启动了,那么就由消费者创建交换机)
        channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT);

        //创建自己的队列
        //                                  非持久化,       独占,         自动删除
        channel.queueDeclare(queueName, false, true, true, null);

        //将队列绑定到绑定到交换机上
        String[] keys = key.split(",");
        for (String s : keys) {
            channel.queueBind(queueName, exchangeName, s);
        }

        //处理消息的回调对象
        DeliverCallback deliverCallback = new DeliverCallback() {
            @Override
            public void handle(String consumerTag, Delivery message) throws IOException {
                byte[] body = message.getBody();
                String msg = new String(body);
                System.out.println(queueName + " >> 收到消息:" + msg);
            }
        };
        //消费者取消接收消息的回调对象(不再从队列接收消息的回调)
        CancelCallback cancelCallback = new CancelCallback() {
            @Override
            public void handle(String consumerTag) throws IOException {

            }
        };
        //接收消息
        channel.basicConsume(queueName, true, deliverCallback, cancelCallback);
        System.out.println(queueName + " >> 启动");
    }
}

主题模式

生产者

package com.example.rabbitmq.topic;

import com.example.rabbitmq.Utils;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;

import java.io.IOException;
import java.util.Scanner;
import java.util.concurrent.TimeoutException;

/**
 * 题模式,类似路由模式
 * 生产者只需要往交换机内投递消息,每个绑定了此交换机的消费者跟自动根据路由键的类型来收取指定消息
 *
 * @version 0.0.1
 * @Author: LianZuoYang
 * @Create: 2022-08-27 10:55
 */
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        Scanner scanner = new Scanner(System.in);
        //自定义一个交换机名称(需要跟消费者那边一致)
        String exchangeName = "topic_logs";
        //连接RabbitMQ
        Channel channel = Utils.getChannel();//通信通道
        //创建交换机 交换机类型为Fanout,此交换机是广播模式的,即所有绑定了此交换机的队列都会收到消息
        channel.exchangeDeclare(exchangeName, BuiltinExchangeType.TOPIC);

        while (true) {

            System.out.print("输入路由键:");
            String k = scanner.nextLine();
            System.out.print("输入消息:");
            String s = scanner.nextLine();
            //向交换机发送数据,此时需要带上 路由键 用以区分消息类型,才能分发到不同的队列
            System.out.println("发送 " + k + " >> " + s);
            channel.basicPublish(exchangeName, k, null, ("key:" + k + " >> " + s).getBytes());
            System.out.println("-----------------------------------------------");
        }
    }
}

消费者

package com.example.rabbitmq.topic;

import com.example.rabbitmq.Utils;
import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 主题模式,类似路由模式(支持 * # 通配符)
 * 通配符  * 可以匹配一段
 * 通配符  # 可以匹配所有
 * 如  路由键 a.b.c.d   >> a.*.c.*  可以匹配
 *                     >> a.#      可以匹配
 * 消费者需要自己创建队列,绑定到指定的direct类型交换机上,并指定路由键,来收取该键的消息
 *
 * @version 0.0.1
 * @Author: LianZuoYang
 * @Create: 2022-08-27 10:29
 */
public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        //启动消费者.分别订阅不同的路由键
        createConsumer("a.*.c");
        //同时绑定2个键  a.#个人b.#
        createConsumer("a.#,b.#");

        createConsumer("a.b.*");
        //如果没有通配符,实际上跟路由模式是一模一样的
        createConsumer("a.a.a");
        //这个可以接收所有的
        createConsumer("#");

    }

    private static void createConsumer(String key) throws IOException, TimeoutException {
        //自定义一个交换机名称(需要跟生产者那边一致)
        String exchangeName = "topic_logs";

        //在此模式下,队列名应当使用随机名称,此处为了方便观察,用index生成
        String queueName = "queue_" + key;

        Channel channel = Utils.getChannel();//通信通道

        //创建交换机(可能消费者先启动了,那么就由消费者创建交换机)
        channel.exchangeDeclare(exchangeName, BuiltinExchangeType.TOPIC);

        //创建自己的队列
        //                                  非持久化,       独占,         自动删除
        channel.queueDeclare(queueName, false, true, true, null);

        //将队列绑定到绑定到交换机上
        String[] keys = key.split(",");
        for (String s : keys) {
            channel.queueBind(queueName, exchangeName, s);
        }

        //处理消息的回调对象
        DeliverCallback deliverCallback = new DeliverCallback() {
            @Override
            public void handle(String consumerTag, Delivery message) throws IOException {
                byte[] body = message.getBody();
                String msg = new String(body);
                System.out.println(queueName + " >> 收到消息:" + msg);
            }
        };
        //消费者取消接收消息的回调对象(不再从队列接收消息的回调)
        CancelCallback cancelCallback = new CancelCallback() {
            @Override
            public void handle(String consumerTag) throws IOException {

            }
        };
        //接收消息
        channel.basicConsume(queueName, true, deliverCallback, cancelCallback);
        System.out.println(queueName + " >> 启动");
    }
}

使用MQ来进行远程RPC调用

在MQ中使用RPC调用,那么服务器跟客户端,都是生产者,也是消费者

调用模式(通信方式)

提供服务的服务端,自己创建一个队列,如rpc_server
调用方自己创建一个队列,如rpc_client(此处推荐使用路由模式,只接收自己调用的返回信息,下方代码使用的是默认的)

在服务器启动时,创建队列rpc_server,同时消费此队列,来获取调用信息
在客户端中,创建队列rpc_client,同时消费此队列,来获取返回信息
在需要调用方法时,通过往rpc_server队列里发送消息,来达到调用的目的

在发送前,需设置props参数的replyTocorrelationId,设置此条消息的属性,以区分多次调用
参数说明:
replyTo:服务端调用结束后,往此队列发送结果
correlationId:本次调用的唯一id
还有其他参数可以自行查看并使用
关于这么参数的使用还是比较灵活,不一定非要这样设置,可自行设计

参考代码

服务端(被调用方)
package com.example.rabbitmq.rpc;

import com.example.rabbitmq.Utils;
import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
* @version 0.0.1
* @Author: LianZuoYang
* @Create: 2022-08-27 16:59
*/
public class RpcServer {
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建服务端对象与调用队列
        Channel server = Utils.getChannel();
        //提供服务的队列名称(要与客户端那边一致)
        String serverQueueName = "rpc_call";
        //创建调用队列
        server.queueDeclare(serverQueueName, true, false, false, null);
        
        
        //创建消费者,用于接收调用信息
        DeliverCallback deliverCallback = new DeliverCallback() {
            @Override
            public void handle(String consumerTag, Delivery message) throws IOException {
                //收到消息
                byte[] body = message.getBody();
                String param = new String(body);
                //获取调用方信息
                String replyTo = message.getProperties().getReplyTo();
                String correlationId = message.getProperties().getCorrelationId();
                System.out.println("correlationId:" + correlationId + ",param:" + param);
                //处理业务
                long f = f(Integer.parseInt(param));
                //返回结果
                AMQP.BasicProperties properties = new AMQP.BasicProperties().builder().correlationId(correlationId).build();
                server.basicPublish("", replyTo, properties, String.valueOf(f).getBytes());
                System.out.println("correlationId:" + correlationId + ",发送结果:" + f);
            }
        };
        CancelCallback cancelCallback = new CancelCallback() {
            @Override
            public void handle(String consumerTag) throws IOException {
                
            }
        };
        server.basicQos(1);
        server.basicConsume(serverQueueName, true, deliverCallback, cancelCallback);
        System.out.println("服务端已启动");
        
    }
    
    /**
    * 求第n个斐波那契数
    * 1.1.2.3.5.8.13.21.34.55.89.144.......
    */
    public static long f(int n) {
        if (n >= 1 && n <= 2) {
            return 1;
        }
        if (n > 2) {
            //用低效的算法,来模拟耗时运算
            return f(n - 2) + f(n - 1);
        }
        return 0;
    }
}

客户端(调用方)
package com.example.rabbitmq.rpc;

import com.example.rabbitmq.Utils;
import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.Scanner;
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeoutException;

/**
 * @version 0.0.1
 * @Author: LianZuoYang
 * @Create: 2022-08-27 17:12
 */
public class RpcClient {
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建客户端对象
        Channel client = Utils.getChannel();
        //设置提供服务的队列名称
        String clientQueueName = "rpc_return";
        //设置服务调用后传输返回值的队列
        String serverQueueName = "rpc_call";
        //创建返回数据队列
        client.queueDeclare(clientQueueName, true, false, false, null);

        //创建消费者,用于接收调用的返回信息
        DeliverCallback deliverCallback = new DeliverCallback() {
            @Override
            public void handle(String consumerTag, Delivery message) throws IOException {
                byte[] body = message.getBody();
                String str = new String(body);
                //获取调用ID
                String correlationId = message.getProperties().getCorrelationId();
                System.out.println("收到答复:  " + correlationId + "  >>  " + str);
            }
        };
        CancelCallback cancelCallback = new CancelCallback() {
            @Override
            public void handle(String consumerTag) throws IOException {

            }
        };
        client.basicQos(1);
        //监听此队列,获取调用结果
        client.basicConsume(clientQueueName, true, deliverCallback, cancelCallback);

        Scanner scanner = new Scanner(System.in);
        while (true) {
            //超过40,计算速度就慢了
            System.out.println("请输入:");
            String param = scanner.nextLine();
            String correlationId = UUID.randomUUID().toString();
            AMQP.BasicProperties properties = new AMQP.BasicProperties().builder()
                    .correlationId(correlationId)
                    .replyTo(clientQueueName)
                    .build();
            client.basicPublish("", serverQueueName, properties, param.getBytes());
            System.out.println("发送完毕,correlationId:" + correlationId);
        }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值