消息队列(MQ)rabbitmq的使用

rabbitmq的下载及安装见上一篇博客

一、MQ 简介

1、什么是MQ?

在计算机科学中,消息队列(英语:Message queue)是 一种进程间通信或同一进程的不同线程间的通信方式,软件的贮列用来处理一系列的输入,通常是来自用户。
消息队列提供了 异步的通信协议,每一个贮列中的纪录包含详细说明的数据,包含发生的时间,输入设备的种类,以及特定的输入参数,也就是说:消息的发送者和接收者不需要同时与消息队列互交。消息会保存在队列中,直到接收者取回它。

2、MQ的特点

MQ是消费-生产者模型的一个典型的代表,一端往消息队列中不断写入消息,而另一端则可以读取或者订阅队列中的消息。MQ 和 JMS 类似,但不同的是 JMS是 SUN JAVA 消息中间件服务的一个标准和 API 定义,而 MQ 则是遵循了AMQP 协议的具体实现和产品。

  1. AMQP,即 Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。
  2. JMS,Java 消息服务(Java Message Service)应用程序接口,是一个Java 平台中关于面向消息中间件的 API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。 Java 消息服务是一个与具体平台无关的 API,绝大多数 MOM 提供商都对 JMS 提供支持。
3、使用场景

在项目中,将一些无需即时返回且耗时的操作提取出来,进行了异步处理,而这种异步处理的方式大大的节省了服务器的请求响应时间,从而提高了系统的吞吐量
使用的条件就是不要求消息要实时到达消费者手里,如注册成功后的注册成功的消息,晚几分钟也没啥关系,所以就可以使用消息队列。还有就如双十一订单的处理,高峰期产生的订单可以先放在消息队列中,等待一条一条的处理,怎样就大大的提高了处理订单的能力了,放在后台一步处理即可。
商品的秒杀活动,可以让前1000名的订单进入消息队列,后面的直接返回业务繁忙页面,这样可以解决秒级峰值的处理能力。

二、创建一个管理员用户

1、登录安装好的rabbitmq

主机ip+端口号15672 登录rabbitmq的图形界面管理页面如:192.168.56.123:15672 , 用户名与密码都是上一篇创建的admin
在这里插入图片描述

2、创建一个新的用户角色与授权

进入admin下面,在users页面,下方就要add user的操作啦!填写好用户名与密码及授予的权限,这里授予了管理员权限。点击add user即可完成。
在这里插入图片描述
在这里插入图片描述

3、创建 virtual hosts

用户添加完毕,用户列表显示用户状态是 No Access ,代表用户未进行权限分配,不能进行任何操作,这里创建用户分配权限可以类比数据库中创建用户并分配权限操作,创建道理一样。
在这里插入图片描述
点击右侧的Virtual Hosts,给test创建一个virtual hosts,左下角有个add virtual hosts, 创建个test的virtual hosts。
在这里插入图片描述
发现test显示No users,然后点击/test进入权限分配用户,也就是刚add的virtual hosts分给test用户。
在这里插入图片描述
授权完成后。test的用户也就拥有了test的virtual hosts的节点了。
在这里插入图片描述

三、基于官网的六种消息队列的模型

1、“Hello World” 模型

在这里插入图片描述
发送方的实现,P---->端

/**
 * 消息发送方
 *   发送消息到Mq 指定队列
 */
public class Sender {

    public static final String QUEUE_NAME="simple-queue";
    
    public static void main(String[] args) throws IOException, TimeoutException {

        ConnectionFactory factory = new ConnectionFactory();
        /**
         * 主机ip
         * 客户端连接端口号 5672
         * 设置操作的 virtual host
         * 设置用户名
         * 设置密码
         */
        factory.setHost("192.168.56.123");
        factory.setPort(5672);
        factory.setVirtualHost("/shsxt");
        factory.setUsername("shsxt");
        factory.setPassword("123456");

        // 获取连接对象
        Connection connection = factory.newConnection();
        // 获取通道对象
        Channel channel = connection.createChannel();
        // 声明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        // 声明消息内容
        String msg = "I am a simple message";
        // 发送消息到队列
        channel.basicPublish("", QUEUE_NAME, null, msg.getBytes("utf-8"));
        // 关闭资源
        channel.close();
        connection.close();
    }
}

消费端的实现,C---->端

/**
 * 消息接收方
 *   接收指定队列 消息内容
 */
public class Receiver {

    public static final String QUEUE_NAME="simple-queue";
    
    public static void main(String[] args) throws IOException, TimeoutException {

        ConnectionFactory factory = new ConnectionFactory();
        /**
         * 主机ip
         * 客户端连接端口号 5672
         * 设置操作的 virtual host
         * 设置用户名
         * 设置密码
         */
        factory.setHost("192.168.56.123");
        factory.setPort(5672);
        factory.setVirtualHost("/shsxt");
        factory.setUsername("shsxt");
        factory.setPassword("123456");

        Connection connection = factory.newConnection();
        // 创建通道
        Channel channel = connection.createChannel();
        // 指定队列
        channel.queueDeclare(QUEUE_NAME, false, false , false , null );
        //消费者
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body,"utf-8");
                System.out.println("消费方收到消息-->"+msg);
            }
        };

        channel.basicConsume(QUEUE_NAME,true,consumer);
    }
}
2、简单工作队列模型(两种工作模式1、公平分发,2、轮循方式)

在这里插入图片描述
先把获取连接的操作封装成工具类吧;

public class GetConnection {

    public static Connection getConnection() throws Exception{
        ConnectionFactory factory = new ConnectionFactory();
        /**
         * 主机ip
         * 客户端连接端口号 5672
         * 设置操作的 virtual host
         * 设置用户名
         * 设置密码
         */
        factory.setHost("192.168.56.123");
        factory.setPort(5672);
        factory.setVirtualHost("/shsxt");
        factory.setUsername("shsxt");
        factory.setPassword("123456");

        // 获取连接对象
        return factory.newConnection();
    }
}
a、消息公平分发(fair dispatch)(结果就是三十条消息,第一个消费者处理大约20 个,第二个消费大约10个,因为线程的sleep不同)

发送端的代码实现,publisher---->端

public class Sender {

    public static final String QUEUE_NAME="simple-queue";
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {

        // 获取连接对象
        Connection connection = GetConnection.getConnection();
        
        // 获取通道对象
        Channel channel = connection.createChannel();
        // 声明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        // 声明消息内容
        for (int i=0; i<30; ++i) {
            String msg = "I am a simple message" + i;
            // 发送消息到队列
            channel.basicPublish("", QUEUE_NAME, null, msg.getBytes("utf-8"));
            Thread.sleep(1000);
        }
        // 关闭资源
        channel.close();
        connection.close();
    }
}

两个接收端,Consumer---->端

public class Receiver01 {

    public static final String QUEUE_NAME="simple-queue";
    public static void main(String[] args) throws IOException, TimeoutException {

         // 获取连接对象
        Connection connection = GetConnection.getConnection();
        // 创建通道
        Channel channel = connection.createChannel();
        // 指定队列
        channel.queueDeclare(QUEUE_NAME, false, false , false , null );
        //maximum number of messages that the server will deliver
        channel.basicQos(1);
        //消费者
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body,"utf-8");
                System.out.println("消费方收到消息-->"+msg);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //手动回执
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        // 自动回执 false 禁用自动回执  当消息处理完毕后再执行回执
        channel.basicConsume(QUEUE_NAME,false,consumer);
    }
}
public class Receiver02 {

    public static final String QUEUE_NAME="simple-queue";
    public static void main(String[] args) throws IOException, TimeoutException {

        // 获取连接对象
        Connection connection = GetConnection.getConnection();
        // 创建通道
        Channel channel = connection.createChannel();
        // 指定队列
        channel.queueDeclare(QUEUE_NAME, false, false , false , null );
        //通道产生1条,配置
        channel.basicQos(1);
        //消费者
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body,"utf-8");
                System.out.println("消费方收到消息-->"+msg);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 手动回执
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        // 自动回执 false 禁用自动回执  当消息处理完毕后再执行回执
        channel.basicConsume(QUEUE_NAME,false,consumer);
    }
}
b、消息轮询分发(Round-robin)(结果两个消费者会依次得到消息并处理,对半分到消息)

发送端,publisher---->端

public class Sender {

    public static final String QUEUE_NAME="simple-queue";
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {

        // 获取连接对象
        Connection connection = GetConnection.getConnection();
        // 获取通道对象
        Channel channel = connection.createChannel();
        // 声明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        for (int i=0; i<30; ++i) {
            // 声明消息内容
            String msg = "I am a simple message" + i;
            // 发送消息到队列
            channel.basicPublish("", QUEUE_NAME, null, msg.getBytes("utf-8"));
            System.out.println("生产者产生消息-->"+msg);
            Thread.sleep(1000);
        }
        // 关闭资源
        channel.close();
        connection.close();
    }
}

两个消费端,consumer---->端

public class Receiver01 {

    public static final String QUEUE_NAME="simple-queue";
    public static void main(String[] args) throws IOException, TimeoutException {

       // 获取连接对象
        Connection connection = GetConnection.getConnection();
        // 创建通道
        Channel channel = connection.createChannel();
        // 指定队列
        channel.queueDeclare(QUEUE_NAME, false, false , false , null );
        //消费者
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body,"utf-8");
                System.out.println("消费方01收到消息-->"+msg);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        channel.basicConsume(QUEUE_NAME,true,consumer);
    }
}
public class Receiver02 {

    public static final String QUEUE_NAME="simple-queue";
    public static void main(String[] args) throws IOException, TimeoutException {

         // 获取连接对象
        Connection connection = GetConnection.getConnection();
        // 创建通道
        Channel channel = connection.createChannel();
        // 指定队列
        channel.queueDeclare(QUEUE_NAME, false, false , false , null );
        //消费者
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body,"utf-8");
                System.out.println("消费方02收到消息-->"+msg);
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        channel.basicConsume(QUEUE_NAME,true,consumer);
    }
}
3、发布/订阅者模型(消息的发布与订阅),fanout类型。结果是两个接收方都能收到消息。

对于微信公众号,相信每个人都订阅过,当公众号发送新的消息后,对于订阅过该公众号的所有用户均可以收到消息,这个场景大家都能明白,同样对于RabbitMq 消息的处理也支持这种消息处理,当生产者把消息投送出去后,不同的消费者均可以对该消息进行消费,而不是消息被一个消费者消费后就立即从队列中删除,对于这种消息处理,我们通常称之为消息的发布与订阅模式,凡是消费者订阅了该消息,均能够收到对应消息进行处理,比较常见的如用户注册操作。模型图如下:
在这里插入图片描述
从图中看到:
1.消息产生后不是直接投送到队列中,而是将消息先投送给 Exchange 交换机,然后消息经过 Exchange 交换机投递到相关队列。
2.多个消费者消费的不再是同一个队列,而是每个消费者消费属于自己的队列。
发送端的代码实现,publisher---->端

public class Sender {

    public static final String EXCHANGE_NAME="fanout";
    public static void main(String[] args) throws Exception {

        // 获取连接对象
        Connection connection = GetConnection.getConnection();
        // 获取通道对象
        Channel channel = connection.createChannel();
        // 声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
        // 声明消息内容
            String msg = "18702511061&fly_tong@163.com" ;
            // 发送消息到队列
            channel.basicPublish(EXCHANGE_NAME, "", null, msg.getBytes("utf-8"));
        // 关闭资源
        channel.close();
        connection.close();
    }
}

接收端的实现,consumer---->端

public class Receiver01 {

    public static final String QUEUE_NAME="sms";
    public static final String EXCHANGE_NAME="fanout";
    public static void main(String[] args) throws Exception {

        Connection connection = GetConnection.getConnection();

        // 创建通道
        Channel channel = connection.createChannel();
        // 声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME,BuiltinExchangeType.FANOUT);
        // 指定队列
        channel.queueDeclare(QUEUE_NAME, false, false , false , null );
        // 绑定队列到交换机
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME , "");
        //消费者
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body,"utf-8");
                System.out.println("短信消费方收到消息-->"+msg);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        // 自动回执
        channel.basicConsume(QUEUE_NAME,true,consumer);
    }
}
public class Receiver02 {

    public static final String QUEUE_NAME="mail";
    public static final String EXCHANGE_NAME="fanout";
    public static void main(String[] args) throws Exception {

        Connection connection = GetConnection.getConnection();
        // 创建通道
        Channel channel = connection.createChannel();
        // 声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
        // 指定队列
        channel.queueDeclare(QUEUE_NAME, false, false , false , null );
        // 绑定队列到交换机
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
        //消费者
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body,"utf-8");
                System.out.println("邮件消费方收到消息-->"+msg);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        // 自动回执
        channel.basicConsume(QUEUE_NAME,true,consumer);
    }
}
4、消息路由模型,direct类型。运行结果为:消费者1只能接收类型为error的消息,消费者2能接收消息类型为error与info的消息。

通常在真正项目开发时会遇到这种情况:在对项目信息输出日志进行收集时,会把日志(error warning,info)分类进行输出,这时通过 ExchangeTypes 中的 direct 类型就可以实现,针对不同的消息,在对消息进行消费时,通过 Exchange types 以及 Routing key 设置的规则 ,便可以将不同消息路由到不同的队列中然后交给不同消费者进行消费操作。
在这里插入图片描述
从图中可以看出:

  1. 生产者产生的消息投给交换机
  2. 交换机投送消息时的 Exchange Types 为 direct 类型
  3. 消息通过定义的 Routing Key 被路由到指定的队列进行后续消费
    消息的方式方的实现,publisher---->端
public class Sender {

    public static final String EXCHANGE_NAME="direct";
    public static void main(String[] args) throws Exception {

        // 获取连接对象
        Connection connection = GetConnection.getConnection();
        // 获取通道对象
        Channel channel = connection.createChannel();
        // 声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        // 声明消息内容
            String msg = "这是一条error信息" ;
            // 发送消息到队列
            channel.basicPublish(EXCHANGE_NAME, "error", null, msg.getBytes("utf-8"));
        // 关闭资源
        channel.close();
        connection.close();
    }
}

消费端的实现,consumer---->端

public class Receiver01 {

    public static final String QUEUE_NAME="error";
    public static final String EXCHANGE_NAME="direct";
    public static void main(String[] args) throws Exception {

        Connection connection = GetConnection.getConnection();

        // 创建通道
        Channel channel = connection.createChannel();
        // 声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME,BuiltinExchangeType.DIRECT);
        // 指定队列
        channel.queueDeclare(QUEUE_NAME, false, false , false , null );
        // 绑定队列到交换机
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME , "error");
        //消费者
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body,"utf-8");
                System.out.println("短信消费方收到消息-->"+msg);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
               /* //手动回执
                channel.basicAck(envelope.getDeliveryTag(), false);*/
            }
        };
        // 自动回执
        channel.basicConsume(QUEUE_NAME,true,consumer);
    }
}
public class Receiver02 {

    public static final String QUEUE_NAME="info_error";
    public static final String EXCHANGE_NAME="direct";
    public static void main(String[] args) throws Exception {

        Connection connection = GetConnection.getConnection();
        // 创建通道
        Channel channel = connection.createChannel();
        // 声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT );
        // 指定队列
        channel.queueDeclare(QUEUE_NAME, false, false , false , null );
        // 绑定队列到交换机
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "info");
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "error");
        //消费者
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body,"utf-8");
                System.out.println("邮件消费方收到消息-->"+msg);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                /*// 手动回执
                channel.basicAck(envelope.getDeliveryTag(), false);*/
            }
        };
        // 自动回执
        channel.basicConsume(QUEUE_NAME,true,consumer);
    }
}
5、topic主题模型,类型topic。(运行结果:消费者1接收query开头的消息,消费者2接三个单词的,中间的为update的消息)

Mq 提供的 topics 主题模式来对消息进行匹配路由,比如在项目开发中,拿商品模块来说,对于商品的查询功能在对商品进行查询时我们将查询消息路由到查询对应队列,而对于商品的添加、更新、删除等操作我们统一路由到另外一个队列来进行处理,此时采用 direct 模式可以实现,但对于维护的队列可能就不太容易进行维护,如果涉及模块很多,此时对应队列数量就很多,此时我们就可以通过 topic 主题模式来对消息路由时进行匹配,通过指定的匹配模式将消息路由到匹配到的队列中进行后续处理。对于routing key 匹配模式定义规则举例如下:

  • routing key 为一个句点号“. ”分隔的字符串(我们将被句点号“. ”分隔开的每一段独立的字符串称为一个单词),如“stock.usd.nyse”、“nyse.vmw”、“quick.orange.rabbit”
  • routing key 中可以存在两种特殊字符“”与“#”,用于做模糊匹配,其中“”用于匹配一个单词,“#”用于匹配多个单词(可以是零个)
    在这里插入图片描述
    以上图中的配置为例:
  • routingKey=”quick.orange.rabbit”的消息会同时路由到 Q1 与 Q2,
  • routingKey=”lazy.orange.fox”的消息会路由到 Q1,Q2,
  • routingKey=”lazy.brown.fox”的消息会路由到 Q2,
  • routingKey=”lazy.pink.rabbit”的消息会路由到 Q2;
  • routingKey=”quick.brown.fox”、routingKey=”orange”、routingKey=”quick.orange.male.rabbit”的消息将会被丢弃,因为它们没有匹配任何 bindingKey。
    发送端的代码实现,publisher---->端
public class Sender {

    public static final String EXCHANGE_NAME="topic";
    public static void main(String[] args) throws Exception {

        // 获取连接对象
        Connection connection = GetConnection.getConnection();
        // 获取通道对象
        Channel channel = connection.createChannel();
        // 声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
        // 声明消息内容
            String msg = "查询的操作" ;
            // 发送消息到队列
            channel.basicPublish(EXCHANGE_NAME, "query.queryById.user", null, msg.getBytes("utf-8"));
        // 关闭资源
        channel.close();
        connection.close();
    }
}

接收端的实现,consumer---->端

public class Receiver01 {

    public static final String QUEUE_NAME="query";
    public static final String EXCHANGE_NAME="topic";
    public static void main(String[] args) throws Exception {

        Connection connection = GetConnection.getConnection();

        // 创建通道
        Channel channel = connection.createChannel();
        // 声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME,BuiltinExchangeType.TOPIC);
        // 指定队列
        channel.queueDeclare(QUEUE_NAME, false, false , false , null );
        // 绑定队列到交换机
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME , "query.#");
        //消费者
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body,"utf-8");
                System.out.println("查询消费方收到消息-->"+msg);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
               /* //手动回执
                channel.basicAck(envelope.getDeliveryTag(), false);*/
            }
        };
        // 自动回执
        channel.basicConsume(QUEUE_NAME,true,consumer);
    }
}
public class Receiver02 {

    public static final String QUEUE_NAME="update";
    public static final String EXCHANGE_NAME="topic";
    public static void main(String[] args) throws Exception {

        Connection connection = GetConnection.getConnection();
        // 创建通道
        Channel channel = connection.createChannel();
        // 声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC );
        // 指定队列
        channel.queueDeclare(QUEUE_NAME, false, false , false , null );
        // 绑定队列到交换机
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "*.update.*");
        //消费者
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body,"utf-8");
                System.out.println("更新消费方收到消息-->"+msg);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                /*// 手动回执
                channel.basicAck(envelope.getDeliveryTag(), false);*/
            }
        };
        // 自动回执
        channel.basicConsume(QUEUE_NAME,true,consumer);
    }
}
6、RPC模型(了解)

MQ 本身是基于异步的消息处理,前面的示例中所有的生产者(P)将消息发送到 RabbitMQ 后不会知道消费者(C)处理成功或者失败(甚至连有没有消费者来处理这条消息都不知道)。但实际的应用场景中,我们很可能需要一些同步处理,需要同步等待服务端将我的消息处理完成后再进行下一步处理。这相当于RPC(Remote Procedure Call,远程过程调用)。在 RabbitMQ 中也支持 RPC。
在这里插入图片描述
服务端代码实现,server---->端

public class RpcServer {
    private final static String QUEUE_NAME = "rpc_queue";

    public static void main(String[] args) throws Exception {
        // 创建连接
        Connection connection = GetConnection.getConnection();
        // 创建通道并定义队列
        final Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 定义消费者
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       BasicProperties properties, byte[] body)
                    throws IOException {
                String num = new String(body, "utf-8");
                System.out.println("接受的消息是:" + num);
                // 获取计算结果
                int result = fib(Integer.parseInt(num));
                System.out.println("计算算的结果:" + result);
                // 獲取返回結果的隊列
                String replyToQueueName = properties.getReplyTo();
                // 声明replyToQueueName
                channel.queueDeclare(replyToQueueName, false, false, false, null);
                // 发送消息到replyToQueueName 队列
                channel.basicPublish("", replyToQueueName, properties, (result + "").getBytes());
            }
        };
        // 執行消費方法
        channel.basicConsume(QUEUE_NAME, true, consumer);
    }

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

客户端的实现,client---->端

public class RpcClient {
    private final static String QUEUE_NAME = "rpc_queue";
    private final static String REPLY_QUEUE = "reply_queue";
    public static void main(String[] args) throws Exception {
        // 创建连接
        Connection connection = GetConnection.getConnection();
        // 创建通道并定义队列
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        final String correlationId = UUID.randomUUID().toString();
        AMQP.BasicProperties replyProps = new AMQP.BasicProperties
                .Builder().replyTo(REPLY_QUEUE)
                .correlationId(correlationId)
                .build();
        // 向队列发送消息
        channel.basicPublish("", QUEUE_NAME, replyProps, "6".getBytes());
        System.out.println("发送成功!");
        // 接受服务器返回结果
        channel.queueDeclare(REPLY_QUEUE, false, false, false, null);
        // 定义消费者
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       com.rabbitmq.client.AMQP.BasicProperties properties, byte[] body) throws IOException {
                String corrId = properties.getCorrelationId();
                if (correlationId.equals(corrId)) {
                    System.out.println("获取服务器的返回结果:" + new String(body, "utf-8"));
                }
            }
        };
        channel.basicConsume(REPLY_QUEUE, true, consumer);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值