RabbitMQ学习笔记

MQ的用法总结就是三点:限流削峰、应用解耦、异步处理

一、RabbitMQ安装

docker run -dit --name demorabbitmq -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 15672:15672 -p 5672:5672 rabbitmq:management

docker ps查看运行状况

运行成功后,进入rabbitmq容器

rabbitmqctl list_users

rabbitmqctl add_user admin admin 创建账号admin,密码也是admin

rabbitmqctl set_user_tags admin administrator 设置用户角色

rabbitmqctl set_permissions -p "/" admin "*" "*" "*" 设置用户具有vhost中所有资源的配置、写、读权限

配置完成进入页面查看效果

docker 运行时已经配置了,安装完后可以直接登录,不用配置上面的东西

192.168.81.130:15672 用户名密码都是admin

二、RabbitMQ的六大核心模式

1、Hello World

生产者发送消息

在pom文件导入依赖

<dependencies>
    <dependency>
        <groupId>com.rabbitmq</groupId>
        <artifactId>amqp-client</artifactId>
        <version>5.8.0</version>
    </dependency>
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.6</version>
    </dependency>
</dependencies>

测试发送消息

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

 查看结果

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_11,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

消费者接收消息 

编写代码

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

测试结果

6f035c85ff4d417c8fdf12f9a4d7a01d.png

 

2、Work Queues

生产者大量发消息给队列,由多个工作线程接收消息,一个消息只能被处理一次,不能重复处理。轮询分发消息。

新建工具类,获取信道

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

 工作线程代码

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

 生产者代码

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

开启生产者和所有工作线程

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_13,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_14,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_14,color_FFFFFF,t_70,g_se,x_16

 消息应答

分为自动应答和手动应答,自动应答如果消费者突然挂掉会导致消息丢失

手动应答:

1、Channel.basicAck(用于肯定确认)

2、RabbitMQ已知道该消息并且成功处理,可以将其丢弃

3、Channel.basicNack(用于否定确认)

Channel.basicReject(用于否定确认),与Channel.basicNack相比少了一个参数multiple(批量应答),不处理该消息了,直接拒绝,可以将其丢弃了

multiple的true和false是不同的意思:

1、true表示批量应答channel上未应答的消息,比如channel上有传送tag的消息5,6,7,8,,当前tag是8,那么此时5-8的这些还未应答的消息就会被确认收到消息应答
2、false同上面相比只会应答tag=8的消息,5,6,7这三个消息依然不会被确认收到消息应答

手动应答的好处是可以批量应答并且减少网络拥堵,而且还可以防止消息丢失

模拟消费者在工作中宕机情况

消费者4设置较长的睡眠时间,用于停止,模拟宕机 

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

消费者3睡眠1秒,模拟正常运行的工作线程 

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

运行三个线程,发送AA,消费者3成功接收,发送BB,在消费者4睡眠期间关闭,消息成功回到消息队列被消费者3接收

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_12,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_13,color_FFFFFF,t_70,g_se,x_16

 watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_13,color_FFFFFF,t_70,g_se,x_16

持久化

1、队列持久化

生产者代码,在生成队列的方法中,第二个参数改为true,如果原来已经创建过这个队列了,则需要删除原来的

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

a7d0215415ac44278725e9579a64c6cd.png

 在RabbitMQ界面可以看到队列已经持久化了

2、消息持久化

生产者代码,在发送消息的方法中,第三个参数设置为

MessageProperties.PERSISTENT_TEXT_PLAIN

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

不公平分发

消费者代码,在处理消息的方法前加上,每个工作线程都要加上,不会轮询处理消息,不公平分发即能者多劳

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

预取值

只要值不为1就是预取值,值为多少就是预取值多少

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_14,color_FFFFFF,t_70,g_se,x_16

发布确认

生产者将信道设置成confirm模式,所有在该信道上面发布的消息都将会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列之后,broker就会发送一个确认给生产者(包含消息的唯一ID),如果消息和队列是可持久化的,那么确认消息会在将消息写入磁盘之后发出,此外 broker也可以设置basic.ack 的multiple域,表示到这个序列号之前的所有消息都已经得到了处理。

1、单个发布确认

生产者代码,在生成信道后设置成confirm模式,每发送一条消息确认一次,耗时长,但是可以知道哪条消息确认,哪条异常

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

2、批量发布确认

生产者代码,在每发送一部分消息后确认,耗时较短,但是无法知道哪条消息异常

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

3、异步发布确认

生产者代码,在发送消息之前,准备监听器addConfirmListener,然后设置成功和失败两个回调函数,耗时短,可靠性高

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

三种确认机制耗时比较,可以看出异步确认耗时最短,而且可靠性高,建议使用

如何处理异步未确认信息?

首先需要一个数据结构用来存储未确认的消息,ConcurrentSkipListMap是一个很好的选择

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

 然后在每发一条消息就把消息的序号和消息存放到该表中

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

 最后在确认消息的回调函数中删除已确认的消息,剩下的就是未确认消息,注意批量确认和单个确认删除方式不一样

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

交换机

1、fanout交换机(扇出交换机)

交换机一定要先创建,如果在消费者中创建就先开启消费者线程,在生产者中创建就先开启生产者线程

本次测试使用生产者创建交换机,在发送消息时指定交换机名称即可,和路由key无关,设不设置消费者都能接收

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

 消费者在接收消息前需要绑定队列和交换机,其他消费者线程代码一样,绑定同一个交换机即可,和路由key无关,路由key不一样也可以接收到

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

测试如下,两个消费者都能接收到消息

 watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_13,color_FFFFFF,t_70,g_se,x_16watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_13,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_14,color_FFFFFF,t_70,g_se,x_16

 2、direct交换机(直接交换机)

直接交换机可以通过指定的路由key把消息发送到绑定了该交换机的该路由key队列中

生产者代码需要声明交换机类型

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

 消费者代码需要在绑定交换机的同时指定路由key

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

 测试结果

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_16,color_FFFFFF,t_70,g_se,x_16

 watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_16,color_FFFFFF,t_70,g_se,x_16

 watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_17,color_FFFFFF,t_70,g_se,x_16

 3、topic交换机(主题交换机)

topic交换机根据路由key来分发消息,可以同时结合扇出和直接交换机

路由key为一个单词列表,由点号分割,例如 " s1.s2.s3 "   " *.s1.* "    "s1.#"

  • * 可以代替一个单词
  • # 可以代替零个或多个单词

生产者代码需要指定交换机类型,和发送的路由key

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

消费者代码需要绑定交换机,并且指定路由key

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

 测试结果

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_17,color_FFFFFF,t_70,g_se,x_16

 watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_18,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_18,color_FFFFFF,t_70,g_se,x_16

死信队列

生产者代码,声明普通交换机和路由key,可以设置过期时间参数

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

普通消费者代码,声明普通队列,队列需要带上参数设置死信交换机和路由key,绑定普通交换机和路由key

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

 死信消费者代码,声明死信队列,绑定死信交换机和路由key即可

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

 死信的几种方式:

1、消息TTL过期:Time to Live ,即生存时间

        模拟过期可以在发送消息时设置过期参数,先启动生产者和两个消费者,然后关闭普通消费者,重启生产者发送消息,时间到后死信消费者接收到消息        

2、队列达到最大长度:

        模拟可以在生成普通队列时设置队列长度,先启动生产者和两个消费者,然后关闭普通消费者,重启生产者发送消息,超过队列长度的消息被死信消费者接收

3、消息被拒绝:

        模拟可以在消费者代码中,把接收消息改为手动提交(false),然后在接收消息的接口实现中拒绝消息,并且不让消息重回队列(false)

延迟队列

1、准备工作

在pom文件中添加依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.73</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
<!--        swag依赖-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit-test</artifactId>
            <scope>test</scope>
        </dependency>

编写配置类文件

swag配置类

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

如果swag报错,可以尝试降低springboot版本,或者在启动类上加上@EnableWebMvc注解

 RabbitMQ配置类

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16 如果存在队列,需要在RabbitMQ网站先删除,如果报错或者接收不到消息,查看配置是否出错

 生产者代码

因为配置类里已经配置好了路由器和队列,这里只要发送消息即可

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

 消费者代码

监听死信队列

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

上面这种方法有局限性,设置的TTL是固定的,如果需要设置其他的TTL还需要在配置类里再定义一条队列,这样很不方便,所以我们直接在配置类中定义一条没有设置TTL的队列

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

在生产者中定义TTL,注意时间的单位是ms

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAc2h1YWlmZWlmZWlh,size_20,color_FFFFFF,t_70,g_se,x_16

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值