RabbitMQ文档(4)

1 篇文章 0 订阅
1 篇文章 0 订阅

rabbitMq

一、 MQ的重要性

最近mq越来越火,很多公司在用,很多人在用,其重要性不言而喻。但是如果我让你回答下面的这些问题:

  • 我们为什么要用mq?
  • 引入mq会多哪些问题?
  • 如何解决这些问题?

二、MQ的概述

MQ,Message Queue,是一种提供消息队列服务的中间件,也称为消息中间件,是一套提供了消息生
产、存储、消费全过程的软件系统,是在消息的传输过程中保存消息的容器,多用于分布式之间进行通信,消息即数据。一般消息的体量不会很大。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cGsKp2mn-1666225760709)(C:\Users\adminst\AppData\Roaming\Typora\typora-user-images\image-20221009143552062.png)]

小结

  • MQ消息队列,存储消息的中间键
  • 分布式系统通信两种方式:直接远程调用和借助第三方完成间接通信
  • 发送方称为生产者,接收方称为消费者

三、MQ的优势和劣势

优势:

  • 应用解耦
  • 异步同速
  • 削峰填谷

劣势:

  • 系统可用性降低
  • 系统复杂度提高
  • 一致性问题

1.1应用解耦在这里插入图片描述

在这里插入图片描述

系统的耦合性越高,容错性就越低,可维护性就越低

]

使用MQ使得应用之间解耦,提升了容错性和可维护性

1.2异步提速

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sMftZaCD-1666226283058)(https:/在这里插入图片描述]

一个下单操作时间耗时:20+300+300+300=920ms

用户点击完下单按钮后,需要等待920ms才能得到下单响应,太慢了

在这里插入图片描述

用户点击完下单按钮后,只需要等待25ms就能得到下单响应(20+5)=25ms

提升用户体验和系统吞吐量(单位时间内处理请求的数目)

1.3削峰填谷

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

使用了MQ之后,限制消费消息的速度为1000,这样一来,高峰期产生的数据势必会被积压在MQ中,高峰就被削掉了,但是因为消息积压,在高峰期过后的一段时间内,消费消息的速度还是会维持在1000,直到消费完积压的消息,这就叫“填谷”

使用MQ之后,可以提高系统稳定性

  • 应用解耦:提高系统容错性和可维护性
  • 异步同速:提升用户体验和系统吞吐量
  • 削峰填谷:提高系统稳定性

系统的耦合性越高,容错性就越低,可维护性越低

1.4MQ的劣势

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3TY8Nub6-1666225760718)(C:\Users\adminst\AppData\Roaming\Typora\typora-user-images\image-20221009150638139.png)]

  • 系统可用性降低
  • 系统引入的外部依赖越多,系统稳定性越差。一旦 MQ 宕机,就会对业务造成影响。

2.2.2 系统复杂度提高
MQ 的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在是通过 MQ 进行异步调用。如何保证消息没有被重复消费?怎么处理消息丢失情况?那么保证消息传递的顺序性?

2.2.3 一致性问题
A 系统处理完业务,通过 MQ 给B、C、D三个系统发消息数据,如果 B 系统、C 系统处理成功,D 系统处理失败。如何保证消息数据处理的一致性?

真实场景和面试当中经常遇到

小结
MQ 有优势也有劣势,那么使用 MQ 需要满足什么条件呢?

生产者不需要从消费者处获得反馈。引入消息队列之前的直接调用,其接口的返回值应该为空,这 才让明明下层的动作还没做,上层却当成动作做完了继续往后走,即所谓异步成为了可能。

容许短暂的不一致性。

确实是用了有效果。即解耦、提速、削峰这些方面的收益,超过加入MQ,管理MQ这些成本

四、常见的MQ产品

img

1.1rabbitMQ的简介

2007年,Rabbit技术公司基于AMQP标准开发的RabbitMQ 1.0发布。RabbitMQ采用Erlang语言开发。

Erlang语言由Ericson设计,专门为开发高并发和分布式系统的一种语言,在电信领域使用广泛。

AMQP
AMQP,即advanced Message Queuing Protocol (高级消息队列协议),是一个网络协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端\中间件不同产品,不同的开发语言等条件的限制。2006年,AMQP规范发布。类比HTTP。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qmBHw2xj-1666225760719)(assets/image-20221014091014389.png)]

rabbitMQ的基础架构图

相互分离 逻辑分区的作用

img

1.2RabbitMQ中的相关概念

Broker:接收和分发消息的应用,RabbitMQ Server就是Message Broker

Virtual host:出于多租户和安全因素设计的,把AMQP的基本组件划分到一个虚拟的分组中,/以于网 络中的namespace概念。当多个不同的用户使用同一个RabbitMQ server提供的服务时,可以划分出多个vhost,每个用户在自己的vhost创建exchange / queue等

Connection: publisher / consumer 和 broker 之间的 TCP 连接

Channel:如果次访问 RabbitMQ 都建立一个Connection,在消息量大的时候建立TCP Connection的开销将是巨大的,效率也较低。Channel是在connection内部建立的逻辑连接,如果应用程序支持多线 程,通常每个thread创建单独的channel进行通讯,AMQP method包含了channel id帮助客户端和 message broker识别 channel,所以channel之间是完全隔离的。Channel作为轻量级的Connection 极大减少了操作系统建立TCP connection的开销

Exchange: message到达broker的第一站,根据分发规则,匹配查询表中的routing key,分发消息到 queue 中去。常用类型有:direct (point-to-point), topic (publish-subscribe) and fanout (multicast)

Queue:消息最终被送到这里等待consumer取走

Binding: exchange 和 queue 之间的虚拟连接,binding 中可以包含 routing key。Binding 信息被保存到exchange中的查询表中,用于message的分发依据

1.3RabbitMQ提供6种工作模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h50GckBm-1666225760720)(C:\Users\adminst\AppData\Roaming\Typora\typora-user-images\image-20221011202609669.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nm14VJhV-1666225760720)(C:\Users\adminst\AppData\Roaming\Typora\typora-user-images\image-20221011202642162.png)]

  1. 简单模式
  2. work queues
  3. publish\subscribe发布与订阅模式
  4. Routing路由模式
  5. Topics主题模式
  6. RPC远程调用模式(远程调用,不太算MQ)

小结

  1. rabbitMQ是基于AMQP协议使用的Erlang语言开发的一款消息队列产品
  2. RabbitMQ提供了6中工作模式
  3. AMQP是协议,类比HTTP
  4. JMS是API规范接口,类比JDBC

五、RabbitMq的安装

1.安装准备工作
这里通过官网下载需要的版本:RabbitMQ官方网址
鉴于官网访问下载比较慢,贴一个云盘地址:百度云盘地址

rabbitmq官网首页

进入官网向下拉,找到 Download + Installation 下载+安装,点击进入。

在这里插入图片描述

在新页面找到右侧,Install Windows 安装windows版本

在这里插入图片描述

然后页面下拉,找到 Dependencies 依赖关系

在这里插入图片描述

选择otp_win64_24.1.7.exe 我的系统是64位的,所以下载win64

在这里插入图片描述

这里笔者强调一下。我安装的是最新版本。如果你们觉得最新版可能存在未知bug或不兼容问题,可以选择自己想要下载的对应版本即可。

在这里插入图片描述

这里我截图一部分版本参照表,其他的可以自行查看

在这里插入图片描述

2.开始安装
第一步:找到我们下载的软件位置。
第二步:先安装otp_win64_24.1.7.exe

在这里插入图片描述

第三步:鼠标右键以管理员方式运行

在这里插入图片描述

第四步:接着选取要安装的路径,然后一路傻瓜式安装 next 下一步,安装即可。

【注意】不要安装在中文或带空格的文件路径下

第五步:配置系统环境变量

在这里插入图片描述

右键此电脑 - 属性 - 高级系统设置 - 环境变量

接着打开 - 此电脑(文件资源管理器) 找到刚刚我们安装的 erl - 24.1.7 文件 bin 目录下,复制路径 ctrl+c

在这里插入图片描述

切换窗口到环境变量,找到系统变量 path - 编辑

在这里插入图片描述

新建 - ctrl + v 粘贴刚才我们复制的路径,然后三次确定,关闭环境变量窗口

在这里插入图片描述

第六步:安装 RabbitMQ

在这里插入图片描述

右键管理员运行,然后选择安装路径,接着一路 next 下一步,遇到弹窗点允许,没有弹窗则无视。
【注意】不要安装在中文或带空格的文件路径下

第七步:安装完成后找到安装文件路径,找到 sbin 目录下,全选路径 输入 cmd

在这里插入图片描述

打开cmd命令窗口。

在这里插入图片描述

运行下面命令,回车运行。

rabbitmq-plugins enable rabbitmq_management

由于我已经安装过了,所以贴一张。运行成功的图片

在这里插入图片描述

第八步:打开任务资源管理器。win11 快捷键 Ctrl+Shift+Esc,找到rabbitmq服务右键重新启动。

在这里插入图片描述

3.结束安装
打开浏览器。访问 http://127.0.0.1:15672

在这里插入图片描述

出现管理页面:
账号:guest
密码:guest
登录成功后。进入下面页面即代表安装成功。至此大功告成。是不是很简单!

在这里插入图片描述

六、RabbitMq管理台的使用

七、RabbitMq快速入门-生产者 消费者

1、HelloWorld模式-简单队列模式

需求:使用简单的模式完成消息传递

  • 步骤
  • 创建工程(生产者,消费者)
  • 分别添加依赖
  • 编写生产者发送消息
  • 编写消费者接收消息

创建消息的⽣产者

编写⽣产者

package com.chen.producer;

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

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

public class Produce_WorkQueues {
    //hello word 的生产者
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2.设置参数
        factory.setHost("192.168.0.237");//ip默认是localhost
        factory.setPort(5672);//端口,默认是5672
//        factory.setVirtualHost("/testhost");//虚拟机的默认值/
        factory.setUsername("admin");//用户名,密码默认都是guest
        factory.setPassword("admin");
        //3.创建连接connection
        Connection connection= factory.newConnection();
        //4.创建Channel
        Channel channel = connection.createChannel();
        //5.创建队列Queue
        /**
         * queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
         * 1.queue:队列名称
         * 2.durable:是否持久化,当MQ重启之后,还在
         * 3.exclusive:
         *         *是否独占,只能有一个消费者监听这个队列
         *         *当connection关闭时,是否删除队列
         * 4.autoDelete:是否自动删除,当没有Consumer时,自动删除
         * 5.arguments:参数
         */
        //如果没有一个名叫hellomq的队列,则会创建该队列,如果有,则不会创建
        channel.queueDeclare("work_queues",true,false,false,null);
        /**
         *  basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
         *  参数:
         *  1.exchange:交换机名称。简单模式下交换机会使用默认的“”
         *  2.routingKey:路由名称
         *  3.props:配置信息
         *  4.body:发送消息数据
         */
        for (int i = 1; i <=10 ; i++) {
            String body = i+"hello_mq~~~";
            //6.发送消息
            channel.basicPublish("","work_queues",null,body.getBytes());
        }

        //7.释放资源
        channel.close();
        connection.close();
    }
}

创建消息的消费者

编写消费者

package com.chen.consumer;

import com.rabbitmq.client.*;

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

public class Consumer_HelloWord {
    //hello word 的消费者
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2.设置参数
        factory.setHost("192.168.0.237");//ip默认是localhost
        factory.setPort(5672);//端口,默认是5672
//        factory.setVirtualHost("/testhost");//虚拟机的默认值/
        factory.setUsername("admin");//用户名,密码默认都是guest
        factory.setPassword("admin");
        //3.创建连接connection
        Connection connection= factory.newConnection();
        //4.创建Channel
        Channel channel = connection.createChannel();
        //5.创建队列Queue
        /**
         * queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
         * 1.queue:队列名称
         * 2.durable:是否持久化,当MQ重启之后,还在
         * 3.exclusive:
         *         *是否独占,只能有一个消费者监听这个队列
         *         *当connection关闭时,是否删除队列
         * 4.autoDelete:是否自动删除,当没有Consumer时,自动删除
         * 5.arguments:参数
         */
        //如果没有一个名叫hellomq的队列,则会创建该队列,如果有,则不会创建
        channel.queueDeclare("Hello_MQ",true,false,false,null);
        /**
         * basicConsume(String queue, boolean autoAck, Consumer callback)
         * 参数:
         * 1.queue:队列名称
         * 2.autoAck是否自动确认
         * 3.callback回调对象
         */
        //6.接收消息
        Consumer consumer = new DefaultConsumer(channel){
            /**
             * 回调方法,当收到消息后,会自动执行该方法
             * @param consumerTag :标识
             * @param envelope :获取一些信息,交换机的信息,路由key....
             * @param properties :配置信息
             * @param body :真是的数据
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("consumerTag:"+consumerTag);
                System.out.println("Exchange:"+envelope.getExchange());
                System.out.println("RoutingKey:"+envelope.getRoutingKey());
                System.out.println("body:"+new String(body));
                System.out.println("properties:"+properties);
            }
        };
        channel.basicConsume("Hello_MQ",true,consumer);

        //关闭资源? 不要关闭
    }
}

简单队列的问题:

当多个消费者消费同⼀个队列时。这个时候rabbitmq的公平调度机制就开启了,于是,⽆论消费者的消费能⼒如何,每个消费者都能公平均分到相同数量的消息,⽽不能出现能者多劳的情况。

2.work 队列模式: 能者多劳模式

生产者

消费者先声明⼀次只接收⼀条消息: channel.basicQos(1)

消费者关闭⾃动ack

package com.chen.producer;

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

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

public class Produce_WorkQueues {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2.设置参数
        factory.setHost("192.168.0.237");//ip默认是localhost
        factory.setPort(5672);//端口,默认是5672
//        factory.setVirtualHost("/testhost");//虚拟机的默认值/
        factory.setUsername("admin");//用户名,密码默认都是guest
        factory.setPassword("admin");
        //3.创建连接connection
        Connection connection= factory.newConnection();
        //4.创建Channel
        Channel channel = connection.createChannel();
        //5.创建队列Queue
        /**
         * queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
         * 1.queue:队列名称
         * 2.durable:是否持久化,当MQ重启之后,还在
         * 3.exclusive:
         *         *是否独占,只能有一个消费者监听这个队列
         *         *当connection关闭时,是否删除队列
         * 4.autoDelete:是否自动删除,当没有Consumer时,自动删除
         * 5.arguments:参数
         */
        //如果没有一个名叫hellomq的队列,则会创建该队列,如果有,则不会创建
        channel.queueDeclare("work_queues",true,false,false,null);
        /**
         *  basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
         *  参数:
         *  1.exchange:交换机名称。简单模式下交换机会使用默认的“”
         *  2.routingKey:路由名称
         *  3.props:配置信息
         *  4.body:发送消息数据
         */
        for (int i = 1; i <=10 ; i++) {
            String body = i+"hello_mq~~~";
            //6.发送消息
            channel.basicPublish("","work_queues",null,body.getBytes());
        }

        //7.释放资源
        channel.close();
        connection.close();
    }
}

消费者1

package com.chen.consumer;

import com.rabbitmq.client.*;

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

public class Consumer_WorkQueues1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2.设置参数
        factory.setHost("192.168.0.237");//ip默认是localhost
        factory.setPort(5672);//端口,默认是5672
//        factory.setVirtualHost("/testhost");//虚拟机的默认值/
        factory.setUsername("admin");//用户名,密码默认都是guest
        factory.setPassword("admin");
        //3.创建连接connection
        Connection connection= factory.newConnection();
        //4.创建Channel
        Channel channel = connection.createChannel();
        //5.创建队列Queue
        /**
         * queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
         * 1.queue:队列名称
         * 2.durable:是否持久化,当MQ重启之后,还在
         * 3.exclusive:
         *         *是否独占,只能有一个消费者监听这个队列
         *         *当connection关闭时,是否删除队列
         * 4.autoDelete:是否自动删除,当没有Consumer时,自动删除
         * 5.arguments:参数
         */
        //如果没有一个名叫hellomq的队列,则会创建该队列,如果有,则不会创建
        channel.queueDeclare("work_queues",true,false,false,null);
        /**
         * basicConsume(String queue, boolean autoAck, Consumer callback)
         * 参数:
         * 1.queue:队列名称
         * 2.autoAck是否自动确认
         * 3.callback回调对象
         */
        //6.接收消息
        Consumer consumer = new DefaultConsumer(channel){
            /**
             * 回调方法,当收到消息后,会自动执行该方法
             * @param consumerTag :标识
             * @param envelope :获取一些信息,交换机的信息,路由key....
             * @param properties :配置信息
             * @param body :真是的数据
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//                System.out.println("consumerTag:"+consumerTag);
//                System.out.println("Exchange:"+envelope.getExchange());
//                System.out.println("RoutingKey:"+envelope.getRoutingKey());
                System.out.println("body:"+new String(body));
//                System.out.println("properties:"+properties);
            }
        };
        channel.basicConsume("work_queues",true,consumer);

        //关闭资源? 不要关闭
    }
}

消费者2

package com.chen.consumer;

import com.rabbitmq.client.*;

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

public class Consumer_WorkQueues2 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2.设置参数
        factory.setHost("192.168.0.237");//ip默认是localhost
        factory.setPort(5672);//端口,默认是5672
//        factory.setVirtualHost("/testhost");//虚拟机的默认值/
        factory.setUsername("admin");//用户名,密码默认都是guest
        factory.setPassword("admin");
        //3.创建连接connection
        Connection connection= factory.newConnection();
        //4.创建Channel
        Channel channel = connection.createChannel();
        //5.创建队列Queue
        /**
         * queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
         * 1.queue:队列名称
         * 2.durable:是否持久化,当MQ重启之后,还在
         * 3.exclusive:
         *         *是否独占,只能有一个消费者监听这个队列
         *         *当connection关闭时,是否删除队列
         * 4.autoDelete:是否自动删除,当没有Consumer时,自动删除
         * 5.arguments:参数
         */
        //如果没有一个名叫hellomq的队列,则会创建该队列,如果有,则不会创建
        channel.queueDeclare("work_queues",true,false,false,null);
        /**
         * basicConsume(String queue, boolean autoAck, Consumer callback)
         * 参数:
         * 1.queue:队列名称
         * 2.autoAck是否自动确认
         * 3.callback回调对象
         */
        //6.接收消息
        Consumer consumer = new DefaultConsumer(channel){
            /**
             * 回调方法,当收到消息后,会自动执行该方法
             * @param consumerTag :标识
             * @param envelope :获取一些信息,交换机的信息,路由key....
             * @param properties :配置信息
             * @param body :真是的数据
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//                System.out.println("consumerTag:"+consumerTag);
//                System.out.println("Exchange:"+envelope.getExchange());
//                System.out.println("RoutingKey:"+envelope.getRoutingKey());
                System.out.println("body:"+new String(body));
//                System.out.println("properties:"+properties);
            }
        };
        channel.basicConsume("work_queues",true,consumer);

        //关闭资源? 不要关闭
    }
}

3.发布订阅模式-fanout

对于之前的队列模式,是没有办法解决⼀条消息同时被多个消费者消费。于是使⽤发布订阅模式来实现。

关键步骤:声明交换机、把消息发送到交换机上

生产者

package com.chen.producer;

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

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

public class Producer_PubSub {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2.设置参数
        factory.setHost("192.168.0.237");//ip默认是localhost
        factory.setPort(5672);//端口,默认是5672
//        factory.setVirtualHost("/testhost");//虚拟机的默认值/
        factory.setUsername("admin");//用户名,密码默认都是guest
        factory.setPassword("admin");
        //3.创建连接connection
        Connection connection = factory.newConnection();
        //4.创建Channel
        Channel channel = connection.createChannel();
        /**
         * 参数
         exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete,boolean internal, Map<String, Object> arguments)
         * 1.exchange:交换机名称
         * 2.type:交换机类型
         *     DIRECT("direct"), :定向
         *     FANOUT("fanout"), :扇形(广播),发送消息到每一个与之绑定队列
         *     TOPIC("topic"),   :通配符的形式
         *     HEADERS("headers");:参数匹配
         * 3.durable:是否持久化
         * 4.autoDelete:自动删除
         * 5.internal:内部使用,一般都是false
         * 6.arguments:参数
         */
        String exchangeName = "test_fanout";
        //5.创建交换机
        channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT,true,false,false,null);
        //6.创建队列
        String queue1Name = "test_fanout_queue1";
        String queue2Name = "test_fanout_queue2";
        channel.queueDeclare(queue1Name,true,false,false,null);
        channel.queueDeclare(queue2Name,true,false,false,null);
        //7.绑定队列和交换机
        /**
         * queueBind(String queue, String exchange, String routingKey)
         * 参数:
         * 1.queue:队列名称
         * 2.exchange:交换机的名称
         * 3.routingKey:路由键,绑定规则
         *   如果交换机的类型为fanout,routingKey设置为“ ”
         */
        channel.queueBind(queue1Name,exchangeName,"");
        channel.queueBind(queue2Name,exchangeName,"");
        //8.发送消息
        String body = "日志消息:我调用了findall的方法...日志级别为info...";
        channel.basicPublish(exchangeName,"",null,body.getBytes());
        //9.释放资源
        channel.close();
        connection.close();
    }
}

消费者

关键动作:创建队列 创建交换机 把队列绑定在交换机上 让消费者监听队列

package com.chen.consumer;

import com.rabbitmq.client.*;

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

public class Consumer_PubSub1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2.设置参数
        factory.setHost("192.168.0.237");//ip默认是localhost
        factory.setPort(5672);//端口,默认是5672
//        factory.setVirtualHost("/testhost");//虚拟机的默认值/
        factory.setUsername("admin");//用户名,密码默认都是guest
        factory.setPassword("admin");
        //3.创建连接connection
        Connection connection= factory.newConnection();
        //4.创建Channel
        Channel channel = connection.createChannel();

        String queue1Name = "test_fanout_queue1";
        String queue2Name = "test_fanout_queue2";

        /**
         * basicConsume(String queue, boolean autoAck, Consumer callback)
         * 参数:
         * 1.queue:队列名称
         * 2.autoAck是否自动确认
         * 3.callback回调对象
         */
        //6.接收消息
        Consumer consumer = new DefaultConsumer(channel){
            /**
             * 回调方法,当收到消息后,会自动执行该方法
             * @param consumerTag :标识
             * @param envelope :获取一些信息,交换机的信息,路由key....
             * @param properties :配置信息
             * @param body :真是的数据
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//                System.out.println("consumerTag:"+consumerTag);
//                System.out.println("Exchange:"+envelope.getExchange());
//                System.out.println("RoutingKey:"+envelope.getRoutingKey());
                System.out.println("body:"+new String(body));
                System.out.println("将日志信息打印到控制台.....");
//                System.out.println("properties:"+properties);
            }
        };
        channel.basicConsume(queue1Name,true,consumer);

        //关闭资源? 不要关闭
    }
}

4.routing模式-direct

关键动作:

在⽣产者发送消息时指明routing-key

在消费者声明队列和交换机的绑定关系时,指明routing-key

生产者

package com.chen.producer;

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

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

public class Producer_Routing {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2.设置参数
        factory.setHost("192.168.0.237");//ip默认是localhost
        factory.setPort(5672);//端口,默认是5672
//        factory.setVirtualHost("/testhost");//虚拟机的默认值/
        factory.setUsername("admin");//用户名,密码默认都是guest
        factory.setPassword("admin");
        //3.创建连接connection
        Connection connection = factory.newConnection();
        //4.创建Channel
        Channel channel = connection.createChannel();
        /**
         * 参数
         exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete,boolean internal, Map<String, Object> arguments)
         * 1.exchange:交换机名称
         * 2.type:交换机类型
         *     DIRECT("direct"), :定向
         *     FANOUT("fanout"), :扇形(广播),发送消息到每一个与之绑定队列
         *     TOPIC("topic"),   :通配符的形式
         *     HEADERS("headers");:参数匹配
         * 3.durable:是否持久化
         * 4.autoDelete:自动删除
         * 5.internal:内部使用,一般都是false
         * 6.arguments:参数
         */
        String exchangeName = "test_direct";
        //5.创建交换机
        channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT,true,false,false,null);
        //6.创建队列
        String queue1Name = "test_direct_queue1";
        String queue2Name = "test_direct_queue2";
        channel.queueDeclare(queue1Name,true,false,false,null);
        channel.queueDeclare(queue2Name,true,false,false,null);
        //7.绑定队列和交换机
        /**
         * queueBind(String queue, String exchange, String routingKey)
         * 参数:
         * 1.queue:队列名称
         * 2.exchange:交换机的名称
         * 3.routingKey:路由键,绑定规则
         *   如果交换机的类型为fanout,routingKey设置为“ ”
         */
        //1.队列1的绑定 error
        channel.queueBind(queue1Name,exchangeName,"error");
        //2.队列2的绑定,info error warning
        channel.queueBind(queue2Name,exchangeName,"info");
        channel.queueBind(queue2Name,exchangeName,"error");
        channel.queueBind(queue2Name,exchangeName,"warning");
        //8.发送消息
        String body = "日志消息:我调用了delete的方法...日志级别为error...";
        channel.basicPublish(exchangeName,"error",null,body.getBytes());
        //9.释放资源
        channel.close();
        connection.close();
    }
}

消费者

package com.chen.consumer;

import com.rabbitmq.client.*;

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

public class Consumer_Routing1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2.设置参数
        factory.setHost("192.168.0.237");//ip默认是localhost
        factory.setPort(5672);//端口,默认是5672
//        factory.setVirtualHost("/testhost");//虚拟机的默认值/
        factory.setUsername("admin");//用户名,密码默认都是guest
        factory.setPassword("admin");
        //3.创建连接connection
        Connection connection= factory.newConnection();
        //4.创建Channel
        Channel channel = connection.createChannel();

        String queue1Name = "test_direct_queue1";
        String queue2Name = "test_direct_queue2";

        /**
         * basicConsume(String queue, boolean autoAck, Consumer callback)
         * 参数:
         * 1.queue:队列名称
         * 2.autoAck是否自动确认
         * 3.callback回调对象
         */
        //6.接收消息
        Consumer consumer = new DefaultConsumer(channel){
            /**
             * 回调方法,当收到消息后,会自动执行该方法
             * @param consumerTag :标识
             * @param envelope :获取一些信息,交换机的信息,路由key....
             * @param properties :配置信息
             * @param body :真是的数据
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//                System.out.println("consumerTag:"+consumerTag);
//                System.out.println("Exchange:"+envelope.getExchange());
//                System.out.println("RoutingKey:"+envelope.getRoutingKey());
                System.out.println("body:"+new String(body));
                System.out.println("将日志信息打印到控制台.....");
//                System.out.println("properties:"+properties);
            }
        };
        channel.basicConsume(queue2Name,true,consumer);

        //关闭资源? 不要关闭
    }
}

5.topics模式

在routing模式的基础上,对routing-key使⽤了通配符,提⾼了匹配的范围,增加了可玩性

生产者

package com.chen.producer;

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

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

public class Producer_Topics {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2.设置参数
        factory.setHost("192.168.0.236");//ip默认是localhost
        factory.setPort(5672);//端口,默认是5672
//        factory.setVirtualHost("/testhost");//虚拟机的默认值/
        factory.setUsername("admin");//用户名,密码默认都是guest
        factory.setPassword("admin");
        //3.创建连接connection
        Connection connection = factory.newConnection();
        //4.创建Channel
        Channel channel = connection.createChannel();
        /**
         * 参数
         exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete,boolean internal, Map<String, Object> arguments)
         * 1.exchange:交换机名称
         * 2.type:交换机类型
         *     DIRECT("direct"), :定向
         *     FANOUT("fanout"), :扇形(广播),发送消息到每一个与之绑定队列
         *     TOPIC("topic"),   :通配符的形式
         *     HEADERS("headers");:参数匹配
         * 3.durable:是否持久化
         * 4.autoDelete:自动删除
         * 5.internal:内部使用,一般都是false
         * 6.arguments:参数
         */
        String exchangeName = "test_topic";
        //5.创建交换机
        channel.exchangeDeclare(exchangeName, BuiltinExchangeType.TOPIC,true,false,false,null);
        //6.创建队列
        String queue1Name = "test_topic_queue1";
        String queue2Name = "test_topic_queue2";
        channel.queueDeclare(queue1Name,true,false,false,null);
        channel.queueDeclare(queue2Name,true,false,false,null);
        //7.绑定队列和交换机
        /**
         * queueBind(String queue, String exchange, String routingKey)
         * 参数:
         * 1.queue:队列名称
         * 2.exchange:交换机的名称
         * 3.routingKey:路由键,绑定规则
         *   如果交换机的类型为fanout,routingKey设置为“ ”
         */

        //routing key 系统的名称.日志的级别
        //需求:所有error级别的日志存入数据库,所有order系统的日志存入数据库
        channel.queueBind(queue1Name,exchangeName,"#.error");
        channel.queueBind(queue1Name,exchangeName,"order.*");
        channel.queueBind(queue2Name,exchangeName,"*.*");
        //8.发送消息
        String body = "日志消息:我调用了findall的方法...日志级别为info...";
        channel.basicPublish(exchangeName,"goods.error",null,body.getBytes());
        //9.释放资源
        channel.close();
        connection.close();
    }
}

消费者

package com.chen.consumer;

import com.rabbitmq.client.*;

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

public class Consumer_Topic1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2.设置参数
        factory.setHost("192.168.0.236");//ip默认是localhost
        factory.setPort(5672);//端口,默认是5672
//        factory.setVirtualHost("/testhost");//虚拟机的默认值/
        factory.setUsername("admin");//用户名,密码默认都是guest
        factory.setPassword("admin");
        //3.创建连接connection
        Connection connection= factory.newConnection();
        //4.创建Channel
        Channel channel = connection.createChannel();

        String queue1Name = "test_topic_queue1";
        String queue2Name = "test_topic_queue2";

        /**
         * basicConsume(String queue, boolean autoAck, Consumer callback)
         * 参数:
         * 1.queue:队列名称
         * 2.autoAck是否自动确认
         * 3.callback回调对象
         */
        //6.接收消息
        Consumer consumer = new DefaultConsumer(channel){
            /**
             * 回调方法,当收到消息后,会自动执行该方法
             * @param consumerTag :标识
             * @param envelope :获取一些信息,交换机的信息,路由key....
             * @param properties :配置信息
             * @param body :真是的数据
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//                System.out.println("consumerTag:"+consumerTag);
//                System.out.println("Exchange:"+envelope.getExchange());
//                System.out.println("RoutingKey:"+envelope.getRoutingKey());
                System.out.println("body:"+new String(body));
                System.out.println("将日志信息存入数据库中.....");
//                System.out.println("properties:"+properties);
            }
        };
        channel.basicConsume(queue1Name,true,consumer);

        //关闭资源? 不要关闭
    }
}

package com.chen.consumer;

import com.rabbitmq.client.*;

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

public class Consumer_Topic2 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2.设置参数
        factory.setHost("192.168.0.236");//ip默认是localhost
        factory.setPort(5672);//端口,默认是5672
//        factory.setVirtualHost("/testhost");//虚拟机的默认值/
        factory.setUsername("admin");//用户名,密码默认都是guest
        factory.setPassword("admin");
        //3.创建连接connection
        Connection connection= factory.newConnection();
        //4.创建Channel
        Channel channel = connection.createChannel();

        String queue1Name = "test_topic_queue1";
        String queue2Name = "test_topic_queue2";

        /**
         * basicConsume(String queue, boolean autoAck, Consumer callback)
         * 参数:
         * 1.queue:队列名称
         * 2.autoAck是否自动确认
         * 3.callback回调对象
         */
        //6.接收消息
        Consumer consumer = new DefaultConsumer(channel){
            /**
             * 回调方法,当收到消息后,会自动执行该方法
             * @param consumerTag :标识
             * @param envelope :获取一些信息,交换机的信息,路由key....
             * @param properties :配置信息
             * @param body :真是的数据
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//                System.out.println("consumerTag:"+consumerTag);
//                System.out.println("Exchange:"+envelope.getExchange());
//                System.out.println("RoutingKey:"+envelope.getRoutingKey());
                System.out.println("body:"+new String(body));
                System.out.println("将日志信息打印到控制台.....");
//                System.out.println("properties:"+properties);
            }
        };
        channel.basicConsume(queue2Name,true,consumer);

        //关闭资源? 不要关闭
    }
}

八、spring整合RabbitMQ

1.1引入依赖

创建生产者和消费者工程(相同的步骤)

    <dependencies>
        <!--spring 上下文-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.7.RELEASE</version>
        </dependency>

        <!-- spring 整合rabbit-->
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit</artifactId>
            <version>2.1.8.RELEASE</version>
        </dependency>

        <!-- 单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.7.RELEASE</version>
        </dependency>    
    </dependencies>
    
    <build>
        <plugins>
            <!-- 编译插件包-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

1.2配置文件配置rabbitmq(rabbitmq.properties)

rabbitmq.host=192.168.0.236
rabbitmq.port=5672
rabbitmq.username=admin
rabbitmq.password=admin
rabbitmq.virtual-host=chen

1.3生产者

创建在resources包下创建spring-rabbitmq-producer.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/rabbit
       http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">

    <!--加载配置文件-->
    <context:property-placeholder location="classpath:/rabbitmq.properties"/>

    <!-- 定义rabbitmq connectionFactory 也就是工厂加载配置文件里面的内容 -->
    <rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
                               port="${rabbitmq.port}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"/>


    <!--定义管理交换机、队列-->
    <!--封装之后的admin的工厂-->
    <rabbit:admin connection-factory="connectionFactory"/>

    <!--定义持久化队列,不存在则自动创建;不绑定到交换机则绑定到默认交换机
   默认交换机类型为direct,名字为:"",路由键为队列的名称
   -->
    <!--id:bean的名称
        name:queue的名称
        auto-declare:自动创建
        auto-delete:自动删除,最后一个消费者和该队列断开连接后自动删除
        durable:是否持久化
        exclusive;是否独占
        -->
    <rabbit:queue id="spring_queue" name="spring_queue" auto-declare="true"/>

    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~广播;所有队列都能收到消息~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!--定义广播交换机中的持久化队列,不存在则自动创建-->
    <rabbit:queue id="spring_fanout_queue_1" name="spring_fanout_queue_1" auto-declare="true"/>

    <!--定义广播交换机中的持久化队列,不存在则自动创建-->
    <rabbit:queue id="spring_fanout_queue_2" name="spring_fanout_queue_2" auto-declare="true"/>

    <!--定义广播类型交换机;并绑定上述两个队列-->
    <rabbit:fanout-exchange id="spring_fanout_exchange" name="spring_fanout_exchange" auto-declare="true">
        <rabbit:bindings>
            <rabbit:binding queue="spring_fanout_queue_1"/>
            <rabbit:binding queue="spring_fanout_queue_2"/>
        </rabbit:bindings>
    </rabbit:fanout-exchange>
<!--    <rabbit:direct-exchange name="aa">-->
<!--        <rabbit:bindings>-->
<!--            &lt;!&ndash;direct类型的交换机绑定队列 key:路由key queue:队列名称&ndash;&gt;-->
<!--            <rabbit:binding queue="spring_queue" key="xxx"></rabbit:binding>-->
<!--        </rabbit:bindings>-->
<!--    </rabbit:direct-exchange>-->

    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~通配符;*匹配一个单词,#匹配多个单词 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!--定义广播交换机中的持久化队列,不存在则自动创建-->
    <rabbit:queue id="spring_topic_queue_star" name="spring_topic_queue_star" auto-declare="true"/>
    <!--定义广播交换机中的持久化队列,不存在则自动创建-->
    <rabbit:queue id="spring_topic_queue_well" name="spring_topic_queue_well" auto-declare="true"/>
    <!--定义广播交换机中的持久化队列,不存在则自动创建-->
    <rabbit:queue id="spring_topic_queue_well2" name="spring_topic_queue_well2" auto-declare="true"/>

    <rabbit:topic-exchange id="spring_topic_exchange" name="spring_topic_exchange" auto-declare="true">
        <rabbit:bindings>
            <rabbit:binding pattern="chen.*" queue="spring_topic_queue_star"/>
            <rabbit:binding pattern="chen.#" queue="spring_topic_queue_well"/>
            <rabbit:binding pattern="ghh.#" queue="spring_topic_queue_well2"/>
        </rabbit:bindings>
    </rabbit:topic-exchange>

    <!--定义rabbitTemplate对象操作可以在代码中方便发送消息-->
    <rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
</beans>

1.4hello word模式

在测试类里面

//运行器
@RunWith(SpringJUnit4ClassRunner.class)
//加载配置文件
@ContextConfiguration(locations = "classpath:spring-rabbit-producer.xml")
public class TestProducer {
    //注入RabbitTemplate
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Test
    public void testHelloWord(){
        //发送消息
        //spring_queue是刚刚的spring-rabbitmq-producer.xml文件定义的
        rabbitTemplate.convertAndSend("spring_queue","hello_springmq");
    }

1.5fanout模式

发送完之后两个队列都会有消息

 /**
     * 发送fanout消息
     */
    @Test
    public void testFanout() {
        //2.发送消息
        rabbitTemplate.convertAndSend("spring_fanout_exchange","","spring fanout...");
    }

1.6发送topic的消息

/**
     * 发送topic的消息
     */
    @Test
    public void testTopic() {
        //2.发送消息
        rabbitTemplate.convertAndSend("spring_topic_exchange","chen.haha.lala","spring topic...");
    }

2.1消费者

创建在resources包下创建spring-rabbitmq-consumer.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/rabbit
       http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
    <!--加载配置文件-->
    <context:property-placeholder location="classpath:rabbitmq.properties"/>

    <!-- 定义rabbitmq connectionFactory -->
    <rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
                               port="${rabbitmq.port}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"/>

    <bean id="springQueueListener" class="com.chen.SpringQueueListener"/>
<!--    <bean id="fanoutListener1" class="com.itheima.rabbitmq.listener.FanoutListener1"/>-->
<!--    <bean id="fanoutListener2" class="com.itheima.rabbitmq.listener.FanoutListener2"/>-->
<!--    <bean id="topicListenerStar" class="com.itheima.rabbitmq.listener.TopicListenerStar"/>-->
<!--    <bean id="topicListenerWell" class="com.itheima.rabbitmq.listener.TopicListenerWell"/>-->
<!--    <bean id="topicListenerWell2" class="com.itheima.rabbitmq.listener.TopicListenerWell2"/>-->

    <rabbit:listener-container connection-factory="connectionFactory" auto-declare="true">
        <rabbit:listener ref="springQueueListener" queue-names="spring_queue"/>
<!--        <rabbit:listener ref="fanoutListener1" queue-names="spring_fanout_queue_1"/>-->
<!--        <rabbit:listener ref="fanoutListener2" queue-names="spring_fanout_queue_2"/>-->
<!--        <rabbit:listener ref="topicListenerStar" queue-names="spring_topic_queue_star"/>-->
<!--        <rabbit:listener ref="topicListenerWell" queue-names="spring_topic_queue_well"/>-->
<!--        <rabbit:listener ref="topicListenerWell2" queue-names="spring_topic_queue_well2"/>-->
    </rabbit:listener-container>
</beans>

2.1监听器

1按照定义的路径写监听器 com.chen.SpringQueueListener

2实现MessageListener接口

3输入监听内容

package com.chen;

import org.junit.Test;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;

public class SpringQueueListener implements MessageListener {
    @Override

    public void onMessage(Message message) {
        //打印消息
        System.out.println(new String(message.getBody()));
    }
}

4直接用测试类来加载配置文件

package com.chen;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbit-consumer.xml")
public class ConsumerTest {
    @Test
    public void test1() {
        boolean flag = true;
        while (true){
        }
    }
}

九、spring boot整合Rabbit MQ

1.引入依赖

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-amqp</artifactId>
 </dependency> 

2.编写配置文件

server:
 port: 8090
spring:
 rabbitmq:
 host: 172.16.253.8
 port: 5672
 username: xiaoming
 password: 123456
 virtual-host: java2007

3.使用发布订阅模式

1.1编写消费者

编写配置类

package com.qf.myspringbootconsumer.config;


import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyRabbitConfig {

    private static String EXCHANGE_NAME = "my_boot_fanout_exchange";
    private static String QUEUE_NAME = "my_boot_fanout_queue1";

    /*
    声明交换机
     */
    @Bean
    public FanoutExchange exchange(){
        return new FanoutExchange(EXCHANGE_NAME,true,false);
    }

    /*
    声明队列
     */
    @Bean
    public Queue queue(){
        return new Queue(QUEUE_NAME,true,false,false);
    }

    /*
    声明绑定关系
     */
    @Bean
    public Binding queueBinding(Queue queue,FanoutExchange fanoutExchange){
        return BindingBuilder.bind(queue).to(fanoutExchange);
    }


}

编写消费消息的方法

关键:使⽤该注解来指定监听的队列@RabbitListener(queues =“my_boot_fanout_queue”)

package com.qf.myspringbootconsumer;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class MyConsumer {

    /*
    监听队列:当队列中有消息,则监听器工作,处理接收到的消息
     */
    @RabbitListener(queues = "my_boot_fanout_queue1")
    public void process(Message message){
        byte[] body = message.getBody();
        System.out.println("接收到的消息:"+new String(body));
    }

}

1.2编写生产者

编写配置类

package com.qf.my.spring.boot.producer.config;

import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class MyRabbitConfig {

    private static String EXCHANGE_NAME = "my_boot_fanout_exchange";
    /**
     * 声明交换机
     * @return
     */
    @Bean
    public FanoutExchange exchange(){
        return new FanoutExchange(EXCHANGE_NAME,true,false);
    }




}

使⽤RabbitTemplate发送消息

package com.qf.my.spring.boot.producer;

import com.qf.my.spring.boot.producer.config.MyRabbitConfig;
import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class MySpringBootProducerApplicationTests {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    void testSendMessage(){
        String message = "hello spring boot message";
        rabbitTemplate.convertAndSend("my_boot_fanout_exchange","",message);
    }


}

4.使用topic模式

topic模式相⽐发布订阅模式,多了routing-key的使⽤

1.1调整消费者配置类
package com.qf.my.spring.boot.topic.consumer.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyRabbitConfig {

    /*
    声明队列、交换机、绑定关系(routing-key)
     */
    private static String QUEUE_NAME = "my_boot_topic_queue";
    private static String EXCHANGE_NAME = "my_boot_topic_exchange";

    //声明队列
    @Bean
    public Queue queue(){
        return new Queue(QUEUE_NAME,true,false,false);
    }
    //声明交换机
    @Bean
    public TopicExchange topicExchange(){
        return new TopicExchange(EXCHANGE_NAME,true,false);
    }
    //声明绑定关系
    @Bean
    public Binding queueBinding(Queue queue,TopicExchange topicExchange){
        return BindingBuilder.bind(queue).to(topicExchange).with("product.*");
    }


}

1.2编写生产者

调整生产者的配置

package com.qf.my.spring.boot.topic.producer.config;

import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author Thor
 * @公众号 Java架构栈
 */
@Configuration
public class MyRabbitConfig {

    private static String EXCHANGE_NAME = "my_boot_topic_exchange";
    //声明交换机
    @Bean
    public TopicExchange topicExchange(){
        return new TopicExchange(EXCHANGE_NAME,true,false);
    }

}

发布时携带routingkey

package com.qf.my.spring.boot.topic.producer;

import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.IOException;

@SpringBootTest
class MySpringBootTopicProducerApplicationTests {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    void testSendMessage() {
        String message = "hello topic message";
        rabbitTemplate.convertAndSend("my_boot_topic_exchange","product.add",message);
    }

}

5.手动ACK的实现

在配置⽂件中添加⼿动ack的配置

server:
  port: 8091
spring:
  rabbitmq:
    host: 192.168.0.236
    port: 5672
    username: admin
    password: admin
    virtual-host: chen
    listener:
      simple:
        acknowledge-mode: manual

在消费者中进⾏⼿动ack

package com.qf.my.spring.boot.topic.consumer;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * @author Thor
 * @公众号 Java架构栈
 */
@Component
public class MyConsumer {

    @RabbitListener(queues = "my_boot_topic_queue")
    public void process(Message message, Channel channel) throws IOException {
        String id = message.getMessageProperties().getHeader("spring_returned_message_correlation");
        //if(!redisTemplate.ifAbsent(id)){ return "消息已被消费"}
        System.out.println(message.toString());
        //手动ack,告知broker要签收的消息的id(deliveryTag)
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }
}

十、RabbitMQ的高级特性

1.消息的可靠性投递

1.通过confirm机制保证⽣产者消息能够投递到MQ

1.1通过confirm机制保证⽣产者消息能够投递到MQ

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-onblaw1d-1666225760722)(C:\Users\adminst\AppData\Roaming\Typora\typora-user-images\image-20221012161010403.png)]

  • 在spring项⽬中做confirm
package com.qf.producer.confirm;

import com.qf.producer.util.RabbitUtil;
import com.rabbitmq.client.*;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

/**
 * Confirm机制
 * @author Thor
 * @公众号 Java架构栈
 */
public class MyProducer {

    //定义交换机的名称
    private static String EXCHANGE_NAME = "my_topic_exchange";

    public static void main(String[] args) throws Exception {
        Connection connection = RabbitUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.exchangeDeclare(EXCHANGE_NAME,"topic",true);
        //开启confirm机制
        channel.confirmSelect();
        //设置confirm的监听器
        channel.addConfirmListener(new ConfirmListener() {
            //当消息被broker签收了,会回调此方法
            @Override
            public void handleAck(long deliveryTag, boolean multiple) throws IOException {
                System.out.println("消息已经成功投递");
            }
            //当消息没有被broker签收了,会回调此方法
            @Override
            public void handleNack(long deliveryTag, boolean multiple) throws IOException {
                //开启重试机制,重试达到阈值 则人工介入。
                System.out.println("消息投递失败");
            }
        });
        //设置return机制
        channel.addReturnListener(new ReturnListener() {
            @Override
            public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //当消息没有到达队列会调用此方法
                System.out.println("消息没有到达队列");
            }
        });

        String message = "hello confirm message";
        //如果要开启return,则mandatory必须设置成true
        channel.basicPublish(EXCHANGE_NAME,"add",true,null,message.getBytes(StandardCharsets.UTF_8));


    }
}

  • 在springboot中如何实现步骤一:
步骤一:修改生产者的配置
server:
 port: 9090
spring:
 rabbitmq:
 host: 172.16.253.8
 port: 5672
 username: xiaoming
 password: 123456
 virtual-host: java2007
 publisher-confirm-type: correlated

publisher-confirm-type:有三种配置:

  • simple:简单的执⾏ack的判断;在发布消息成功后使⽤rabbitTemplate调⽤waitForConfirms或waitForConfirmsOrDie⽅法等待broker节点返回发送结果,根据返回结果来判断下⼀步的逻辑。但是要注意的是当waitForConfirmsOrDie⽅法如果返回false则会关闭channel
  • correlated: 执⾏ack的时候还会携带数据(消息的元数据);
  • none: 禁⽤发布确认模式, 默认的
步骤⼆:编写⼀个ConfirmCallback的实现类(监听器),并注⼊到RabbiTemplate
package com.qf.my.spring.boot.topic.producer.confirm;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Objects;

/**
 * @author Thor
 * @公众号 Java架构栈
 */
@Component
public class MyConfirmCallback implements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnCallback{

    @Autowired
    private RabbitTemplate rabbitTemplate;

    //把监听器注入到RabbitTemplate
    @PostConstruct
    public void init(){
        rabbitTemplate.setConfirmCallback(this);
        rabbitTemplate.setReturnCallback(this);
    }

    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        //获得消息的id
        String id = "";
        if(Objects.nonNull(correlationData)){
            id = correlationData.getId();
        }
        if(ack){
            //消息投递成功
            System.out.println("消息投递成功,id:"+id);
        }else{
            //消息投递失败,存入到缓存中,通过定时任务,定时重发。
            System.out.println("消息投递失败,原因:"+cause);
        }
    }

    /**
     * 当消息没有传递到队列的时候,回调的方法
     * @param message
     * @param replyCode
     * @param replyText
     * @param exchange
     * @param routingKey
     */
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        System.out.println("消息:"+new String(message.getBody())+",没有成功投递到队列");
    }
}

2.通过return机制保证消息在rabbitmq中能够****成功的投递到队列⾥

⽣产者将消息投递到mq的交换机上——Confirm机制来保证的。

如果交换机没办法将消息投递到队列上,就可以通过Return机制来进⾏重试。

1.spring项⽬中

package com.qf.producer.confirm;

import com.qf.producer.util.RabbitUtil;
import com.rabbitmq.client.*;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

/**
 * Confirm机制
 * @author Thor
 * @公众号 Java架构栈
 */
public class MyProducer {

    //定义交换机的名称
    private static String EXCHANGE_NAME = "my_topic_exchange";

    public static void main(String[] args) throws Exception {
        Connection connection = RabbitUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.exchangeDeclare(EXCHANGE_NAME,"topic",true);
        //开启confirm机制
        channel.confirmSelect();
        //设置confirm的监听器
        channel.addConfirmListener(new ConfirmListener() {
            //当消息被broker签收了,会回调此方法
            @Override
            public void handleAck(long deliveryTag, boolean multiple) throws IOException {
                System.out.println("消息已经成功投递");
            }
            //当消息没有被broker签收了,会回调此方法
            @Override
            public void handleNack(long deliveryTag, boolean multiple) throws IOException {
                //开启重试机制,重试达到阈值 则人工介入。
                System.out.println("消息投递失败");
            }
        });
        //设置return机制
        channel.addReturnListener(new ReturnListener() {
            @Override
            public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //当消息没有到达队列会调用此方法
                System.out.println("消息没有到达队列");
            }
        });

        String message = "hello confirm message";
        //如果要开启return,则mandatory必须设置成true
        channel.basicPublish(EXCHANGE_NAME,"add",true,null,message.getBytes(StandardCharsets.UTF_8));


    }



}

注意,如果要开启return机制的话。需要把mandatory设置成true。

2.springboot中使⽤

步骤一:修改配置文件

server:
  port: 9002
spring:
  rabbitmq:
    host: 192.168.0.236
    username: admin
    password: admin
    virtual-host: chen
    port: 5672
    # 开启confirm simple:简单的执⾏ack的判断;correlated: 执⾏ack的时候还会携带数据;none: 不ack 默认的
    publisher-confirm-type: correlated
    # 开启return机制
    publisher-returns: true


步骤⼆:在监听类中实现RabbitTemplate.ReturnCallback接⼝

package com.qf.my.spring.boot.topic.producer.confirm;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Objects;

/**
 * @author Thor
 * @公众号 Java架构栈
 */
@Component
public class MyConfirmCallback implements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnCallback{

    @Autowired
    private RabbitTemplate rabbitTemplate;

    //把监听器注入到RabbitTemplate
    @PostConstruct
    public void init(){
        rabbitTemplate.setConfirmCallback(this);
        rabbitTemplate.setReturnCallback(this);
    }

    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        //获得消息的id
        String id = "";
        if(Objects.nonNull(correlationData)){
            id = correlationData.getId();
        }
        if(ack){
            //消息投递成功
            System.out.println("消息投递成功,id:"+id);
        }else{
            //消息投递失败,存入到缓存中,通过定时任务,定时重发。
            System.out.println("消息投递失败,原因:"+cause);
        }
    }

    /**
     * 当消息没有传递到队列的时候,回调的方法
     * @param message
     * @param replyCode
     * @param replyText
     * @param exchange
     * @param routingKey
     */
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        System.out.println("消息:"+new String(message.getBody())+",没有成功投递到队列");
    }
}

3.⼿动ack**、nackreject的区别**

1 .不做任何的ack

RabbitMQ会把消息标记成unacked,此时mq是在等待消费者进⾏ack,如果消费者失

去了会话,此时消息会重新回到ready状态,被其他消费者消费。

2.ack

确认签收,之后消息会从队列中剔除。

3.reject

reject就是拒绝此条消息。

reject⼀次只⽀持处理⼀条消息。消息被拒绝掉之后,并且requeue设置成了false,

将会进⼊到死信队列中。如果requeue设置成true,将会重回队列,但是这种情况很

少使⽤。

4.nack

nack和reject相同,只是nack⽀持批量处理多条消息。

package com.qf.consumer.acknack;

import com.qf.consumer.util.RabbitUtil;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * topic的消费者
 * @author Thor
 * @公众号 Java架构栈
 */
public class MyConsumer1 {
    //交换机的名称
    private static String EXCHANGE_NAME = "my_topic_exchange";
    //队列的名称
    private static String QUEUE_NAME = "my_topic_queue_1";

    public static void main(String[] args) throws Exception {
        Connection connection = RabbitUtil.getConnection();
        Channel channel = connection.createChannel();
        //声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME,"topic",true);
        //声明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //绑定
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"product.#");
        //创建消费者
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("product.# 消费者:"+new String(body));
                //手动ack
                //channel.basicAck(envelope.getDeliveryTag(),false);
                //nack
//                channel.basicNack(envelope.getDeliveryTag(),false,false);
                channel.basicReject(envelope.getDeliveryTag(), false);
            }

        };
        //让消费者监听队列
        channel.basicConsume(QUEUE_NAME,false,consumer);

    }

}

5.消息元数据的封装
  • 生产者端
package com.qf.producer.properties;

import com.qf.producer.util.RabbitUtil;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;


public class MyProducer {

    //定义交换机的名称
    private static String EXCHANGE_NAME = "my_topic_exchange";

    public static void main(String[] args) throws Exception {
        Connection connection = RabbitUtil.getConnection();
        Channel channel = connection.createChannel();
        //1.声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME,"topic",true);

        //创建消息的元数据
        Map<String,Object> map = new HashMap<>();
        map.put("name","zhangsan");
        map.put("age",20);
        AMQP.BasicProperties properties = new AMQP.BasicProperties().builder()
                .deliveryMode(2) //消息是否支持持久化:1不支持,2支持
                .messageId(UUID.randomUUID().toString()) //定义消息的业务id
                .expiration("10000") //定义消息的过期时间
                .headers(map)
                .build();


        //2.发送消息
        channel.basicPublish(EXCHANGE_NAME,"product.add.one",properties,"hello topic".getBytes(StandardCharsets.UTF_8));
        System.out.println("消息已发送");
        //3.关闭连接
        channel.close();
        connection.close();

    }
}

  • 消费者端
package com.qf.consumer.properties;

import com.qf.consumer.util.RabbitUtil;
import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.Map;


public class MyConsumer1 {
    //交换机的名称
    private static String EXCHANGE_NAME = "my_topic_exchange";
    //队列的名称
    private static String QUEUE_NAME = "my_topic_queue_1";

    public static void main(String[] args) throws Exception {
        Connection connection = RabbitUtil.getConnection();
        Channel channel = connection.createChannel();
        //声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME,"topic",true);
        //声明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //绑定
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"product.#");
        //创建消费者
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("product.# 消费者:"+new String(body));
                Map<String, Object> map = properties.getHeaders();
                System.out.println(map.get("name"));
                System.out.println(properties.getMessageId());
                //手动ack
                channel.basicAck(envelope.getDeliveryTag(),false);
                //nack
//                channel.basicNack(envelope.getDeliveryTag(),false,false);
            }

        };
        //让消费者监听队列
        channel.basicConsume(QUEUE_NAME,false,consumer);

    }

}

十一、消息的重复消费问题

1.什么是幂等性

幂等性:多次操作造成的结果是⼀致的。对于⾮幂等的操作,幂等性如何保证?——使⽤分布式锁。

在请求⽅式中的幂等性的体现

  • get:多次get 结果是⼀致的
  • post:添加,⾮幂等
  • put:修改:幂等,根据id修改
  • delete:根据id删除,幂等

对于⾮幂等的请求,我们在业务⾥要做幂等性保证。

在消息队列中的幂等性体现

消息队列中,很可能⼀条消息被冗余部署的多个消费者收到,对于⾮幂等的操作,

⽐如⽤户的注册,就需要做幂等性保证,否则消息将会被重复消费。使⽤分布式锁

解决幂等性问题

2.业务代码中实现幂等性

1、⽣产者端修改配置⽂件

server:
  port: 9002
spring:
  rabbitmq:
    host: 192.168.0.236
    username: admin
    password: admin
    virtual-host: chen
    port: 5672
    # 开启confirm simple:简单的执⾏ack的判断;correlated: 执⾏ack的时候还会携带数据;none: 不ack 默认的
    publisher-confirm-type: correlated
    # 开启return机制
    publisher-returns: true


2、⽣产者端传递业务id

@Test
 public void testSendMessage(){
 //业务id
 String id = UUID.randomUUID().toString();
 //封装了业务id的消息元数据
 CorrelationData correlationData = new CorrelationData(id);
 //发送消息,并且携带消息的业务id
 rabbitTemplate.convertAndSend("my_boot_topic_exchange",
 "product.add",
 "hello message",
 correlationData
 );
 }

3、消费者端进⾏业务逻辑判断

/**
 * 消费端的幂等性的实现
 */
 @RabbitListener(queues = "my_boot_topic_queue")
 public void processByMSG(Message message,Channel channel)
throws IOException {
 //如何获得消息的业务id
 String messageId =
message.getMessageProperties().getHeader("spring_returned_message_
correlation");
 //设置分布式锁
 Boolean lock =
redisTemplate.opsForValue().setIfAbsent(messageId, 1,100000,
TimeUnit.MILLISECONDS);
 if(lock){
 //做消费
 System.out.println("添加⽤户成功");
 //⼿动ack
 
channel.basicAck(message.getMessageProperties().getDeliveryTag(),
false);
 }else{
 //不做消费
 System.out.println("已重复消费");
 
channel.basicReject(message.getMessageProperties().getDeliveryTag
(),false);
 }
 }

其中,message的请求头中的这两个键值对分别为:

  • spring_listener_return_correlation:该属性是⽤来确定消息被退回时调⽤哪个监听器
  • spring_returned_message_correlation:该属性是指退回待确认消息的唯⼀标识

十二、死信队列——“延迟队列

1.死信队列的介绍

死信队列 ,让⼀条消息,在满⾜⼀定的条件下,成为死信,会被发送到另⼀个交换机上,再被消费。 这个过程就是死信队列的作⽤。死信队列就可以做出“延迟”队列的效果。⽐如,在订单超时未⽀付 ,将订单状态改成“已取消”,这个操作就可以使⽤死信队列来完成。设置消息的超时时间,当消息超时则消息成为死信,于是通过监听死信队列的消费者来做取消订单的动作。

要掌握两个知识:

  • 消息如何成为死信? 成为死信的条件

  • 怎样创建死信队列,完成死信队列的效果

2.消息成为死信的条件

  • 消息被拒签,并且没有重回队列,消息将成为死信。

  • 消息过期了,消息将成为死信。

  • 队列⻓度有限,存不下消息了,存不下的消息将会成为死信。

3.创建死信队列

关键点:让正常的队列,绑定上死信交换机即可。注意:这个死信交换机实际上也是⼀个正常交换机。

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

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

public class MyProducer {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2.设置参数
        factory.setHost("192.168.0.236");//ip默认的话是localhost
        factory.setPort(5672);//端口号,默认就是5672
        factory.setUsername("admin");
        factory.setPassword("admin");//默认的话是guest
        factory.setVirtualHost("chen");//虚拟机默认的话是/
        //3.创建连接connection
        Connection connection = factory.newConnection();
        //4.创建channel
        Channel channel = connection.createChannel();
        String nomalExchangeName = "chen.nomal.exchange";
        //创建交换机
        channel.exchangeDeclare(nomalExchangeName,"topic",true,false,null);
        //创建消息
        for (int i = 0; i <10 ; i++) {
            String orderId = UUID.randomUUID().toString();
            AMQP.BasicProperties properties = new AMQP.BasicProperties().builder()
                    .deliveryMode(2)
                    .expiration("10000")
                    .messageId(orderId)
                    .build();

            String message = "订单对象";
            //向channel中发消息
            channel.basicPublish(nomalExchangeName,"chen.dlx.add",properties,message.getBytes());
            System.out.println("订单"+orderId+"已创建,等待支付");

        }


    }

}

import com.rabbitmq.client.*;

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

public class MyDlxConsumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.设置连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2.设置参数
        factory.setHost("192.168.0.236");//ip默认的话是localhost
        factory.setPort(5672);//端口号,默认就是5672
        factory.setUsername("admin");
        factory.setPassword("admin");//默认的话是guest
        factory.setVirtualHost("chen");//虚拟机默认的话是/
        //3.创建connection连接
        Connection connection = factory.newConnection();
        //4.创建channel
        Channel channel = connection.createChannel();

        //声明普通交换机,普通队列,死信交换机、死信队列、建立他们之间的关系
        String nomalExchangeName = "chen.nomal.exchange";
        String exchangetype = "topic";
        String nomalQueueName = "chen.nomal.queue";
        String routingkey = "chen.dlx.*";

        //声明死信队列
        String dlxExchangeName = "chen.dlx.exchange";
        String dlxQueueName = "chen.dlx.queue";

        //建立他们之间的关系
        channel.exchangeDeclare(nomalExchangeName,exchangetype,true,false,null);
        Map<String,Object>queueArgs = new HashMap<>();
        queueArgs.put("x-dead-letter-exchange",dlxExchangeName);
        queueArgs.put("x-max-length",4);
        channel.queueDeclare(nomalQueueName,true,false,false,queueArgs);
        channel.queueBind(nomalQueueName,nomalExchangeName,routingkey);

        //创建死信队列
        channel.exchangeDeclare(dlxExchangeName,exchangetype,true,false,null);
        channel.queueDeclare(dlxQueueName,true,false,false,null);
        channel.queueBind(dlxQueueName,dlxExchangeName,"#");

        //创建一个消费死信队列的消费者
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body)
                    throws IOException
            {
                String messageId = properties.getMessageId();
                //根据订单id,去数据库更新该订单的状态已取消
                System.out.println("当前订单"+messageId+"已取消");
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };

        channel.basicConsume(dlxQueueName,false,consumer);

        //拒绝签收消息,让消息成为死信
//        Consumer consumer1= new DefaultConsumer(channel){
//            @Override
//            public void handleDelivery(String consumerTag,
//                                       Envelope envelope,
//                                       AMQP.BasicProperties properties,
//                                       byte[] body)
//                    throws IOException
//            {
//                System.out.println("拒绝签收消息");
//                //注意一定是不能重回队列
//                channel.basicReject(envelope.getDeliveryTag(),false);
//
//            }
//        };
    }
}

 Connection connection = factory.newConnection();
    //4.创建channel
    Channel channel = connection.createChannel();

    //声明普通交换机,普通队列,死信交换机、死信队列、建立他们之间的关系
    String nomalExchangeName = "chen.nomal.exchange";
    String exchangetype = "topic";
    String nomalQueueName = "chen.nomal.queue";
    String routingkey = "chen.dlx.*";

    //声明死信队列
    String dlxExchangeName = "chen.dlx.exchange";
    String dlxQueueName = "chen.dlx.queue";

    //建立他们之间的关系
    channel.exchangeDeclare(nomalExchangeName,exchangetype,true,false,null);
    Map<String,Object>queueArgs = new HashMap<>();
    queueArgs.put("x-dead-letter-exchange",dlxExchangeName);
    queueArgs.put("x-max-length",4);
    channel.queueDeclare(nomalQueueName,true,false,false,queueArgs);
    channel.queueBind(nomalQueueName,nomalExchangeName,routingkey);

    //创建死信队列
    channel.exchangeDeclare(dlxExchangeName,exchangetype,true,false,null);
    channel.queueDeclare(dlxQueueName,true,false,false,null);
    channel.queueBind(dlxQueueName,dlxExchangeName,"#");

    //创建一个消费死信队列的消费者
    Consumer consumer = new DefaultConsumer(channel){
        @Override
        public void handleDelivery(String consumerTag,
                                   Envelope envelope,
                                   AMQP.BasicProperties properties,
                                   byte[] body)
                throws IOException
        {
            String messageId = properties.getMessageId();
            //根据订单id,去数据库更新该订单的状态已取消
            System.out.println("当前订单"+messageId+"已取消");
            channel.basicAck(envelope.getDeliveryTag(),false);
        }
    };

    channel.basicConsume(dlxQueueName,false,consumer);

    //拒绝签收消息,让消息成为死信

// Consumer consumer1= new DefaultConsumer(channel){
// @Override
// public void handleDelivery(String consumerTag,
// Envelope envelope,
// AMQP.BasicProperties properties,
// byte[] body)
// throws IOException
// {
// System.out.println(“拒绝签收消息”);
// //注意一定是不能重回队列
// channel.basicReject(envelope.getDeliveryTag(),false);
//
// }
// };
}
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值