RabbitMQ总结

今年公司开始转型微服务架构,消息队列的选型是RabbitMQ,因此趁这个机会把RabbitMQ好好了解一下。

 

对于为什么选择rabbitmq

对于当前的消息队列的选型 

当前可供选择的 消息队列有很多:rabbitMQ、kafka、rocketMQ 、zeroMQ、ActiveMQ

对于公司选择了rabbitMQ的理由如下:

使用rabbitMQ的目的大部分是子系统间的数据传递与同步,同时系统是toB的系统,对tps的要求不高

rabbitMQ 是用erlang语言编写

是实现了高级消息队列协议(AMQP)的消息中间件

优点:1.社区活跃度高

          2.成熟度高: 可靠性、灵活的路由、集群、事务、高可用的队列、消息排序、问题追踪、可视化管理工具、插件系统等等。

          3.由于是erlang语言开发的,因此高并发支持较好

            等等等

缺点:1.对消息堆积的支持并不好

          2.对消息队列的性能要求非常高的场景 不适合

          3.二次开发困难

            等等等

 

安装RabbitMQ

我安装的版本是3.7.18,需要依赖erlang20.3以上的版本

安装步骤可参考:

https://www.cnblogs.com/dreasky/p/9146494.html

安装遇到的问题:

https://blog.51cto.com/10950710/2139572

安装控制台插件

rabbitmq-plugins enable rabbitmq_management

rabbitMQ的默认端口为5672

控制台的默认访问端口为15672

默认账号密码为guest/guest

如果登录有问题:

将ebin目录下rabbit.app中loopback_users里的<<"guest">>删除

 

rabbitmq的消息传递模型组成

消息生产者、channel、exchange、queue、消息消费者

 

rabbitmq持久化

mq的持久化意义在于当消息队列由于某些原因发生服务停止时,重启服务后,由于持久化的原因,交换器、队列以及消息依旧存在。持久化对于消息队列来说,十分重要。rabbitmq的持久化分为 队列持久化消息持久化

队列持久化

查看Queue的源码,存在属性durable,代表队列是否持久化

RabbitMQ总结_第1张图片

对应控制台可以看到Features上显示了队列的durable属性已开启

RabbitMQ总结_第2张图片

交换器持久化

与队列一致

当队列与交换器持久化后,当重启rabbitMQ后,队列与交换器依旧存在

消息持久化

以下所有的列子都是Spring AMQP实现,Spring AMQP 是对原生的 RabbitMQ 客户端的封装。

Spring AMQP实现 默认是消息持久化,可参考如下:

http://blog.720ui.com/2017/rabbitmq_action_durable/

RabbitMQ总结_第3张图片

 

发送者确认模式

   将消息标记为持久性并不能完全保证不会丢失消息。尽管它告诉RabbitMQ将消息保存到磁盘,但是RabbitMQ接受消息并且尚未保存消息时,还有很短的时间,它可能只是保存到缓存中,而没有真正写入磁盘。如果需要确保消息发送成功,则可以使用发布者确认。

 发送确认分为两步,一是确认是否到达交换器,二是确认是否到达队列。

 配置文件增加:

spring.rabbitmq.publisher-confirms=true

 创建回调函数类,实现RabbitTemplate.ConfirmCallback接口

RabbitMQ总结_第4张图片

设置RabbitTemplate的回调函数属性

RabbitMQ总结_第5张图片

以上即可实现生产者消息确认。

消费者确认模式

默认是消费者确认模式为自动,可以设置为手动模式,并通过api手动确认消息被正确收到。

配置文件

 spring.rabbitmq.listener.simple.acknowledge-mode=manual  // 将消费者确认模式设置为手动

消费者的处理函数中使用channel.basicAck来确认消息收到

   channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); 

RabbitMQ总结_第6张图片

对于入参的解释:

deliveryTag

 它代表了 RabbitMQ 向该 Channel 投递的这条消息的唯一标识 ID,是一个单调递增的正整数,delivery tag 的范围仅限于 Channel

RabbitMQ总结_第7张图片

 

rabbitmq的工作模式

 对于rabbitmq的工作模式,官网介绍有七种:

https://www.rabbitmq.com/getstarted.html

1.简单模式

只使用队列  一个生产者发送消息至队列 一个消费者从队列消费消息

该模式较为简单,就不多介绍了

2.work模式

当一个生产者发送消息至队列,但存在多个消费者监听队列时,则消息会公平的分到每个消费者身上。分配消息的策略为   Round-Robin(轮询)

如果把这个work模式的场景想像成将一大批任务分配个多个工人,但是这种轮询的方式往往不是真正的公平,因为rabbitmq不清楚任务的难易程度,难易程度决定了工人完成这个任务所需的时间。从而可以引入手动确认和当前信道最大预获取(prefetchCount)。

手动确认上文已经说明过了,对于当前信道最大预获取(prefetchCount),说明了当前消费者的最大处理能力,当消费者收到消息后,正在处理,未手动确认后,当正在处理的数量等于prefetchCount时,则不再为消费者发送消息,直至正在处理的数量小于prefetchCount后,再发送消息。

3.发布/订阅模式

创建fanoutExchange

RabbitMQ总结_第8张图片

然后bind queue与exchange

RabbitMQ总结_第9张图片

所有监听这个队列的消费者都会收到消息

消息消费者代码:

RabbitMQ总结_第10张图片

4.路由模式

创建directExchange

RabbitMQ总结_第11张图片

然后bind queue与exchange,绑定时需要带上routingKey。

消息生产者在发送消息时,需要带上routingKey

因为当前队列与交换器绑定时,routingkey为topic.sdp,因此消息会发送到该队列上,若发送消息时,带上的routingkey不一致,则该队列收不到该消息。

5.主题模式

创建topicExchange

然后bind queue与exchange,绑定时需要带上routingKey

主题模式下,routingkey是支持通配的,规则如下:

RabbitMQ总结_第12张图片

*号可以代表任意一个单词,#可以代表零个或是多个单词

消息生产者在发送消息时,同样带上routingkey

由于发送消息的routingkey与binding绑定的routingkey匹配,因此队列能收到该消息,当有多个队列的routingkey匹配时,则这些队列都会收到消息。

6.RPC模式

以下例子为异步rpc模式

需要自行在配置类中添加AsyncRabbitTemplate的bean

RabbitMQ总结_第13张图片

消息生产者发送消息,需要设置回调函数:

RabbitMQ总结_第14张图片

消息的消费者:

RabbitMQ总结_第15张图片

消息的生产者获取消费者的返回值,并在回调函数中处理。

7.消费者确认模式

笔者不太清楚官网把消费者确认看做是一种工作模式,上面已经提到过了,不在赘述。

 

另外还有一块是官网中没有提到的,header exchange

header exchange

创建headerExchange

将exchange与queue绑定,这里可以选择匹配规则,是全部匹配还是部分匹配

RabbitMQ总结_第16张图片

消息生存者发送消息时:

 当匹配规则为部分匹配时,消息的header中 部分匹配,则消息能够到达队列,若匹配规则为全部匹配,则消息不能够达到队列。

RabbitMQ总结_第17张图片

消息消费者获取到消息

RabbitMQ总结_第18张图片

另外,在测试中遇到,控制台报错输出:

Caused by: org.springframework.amqp.AmqpException: No method found for class [B

解决方法:

不要将@RabbitListener(queues = RabbitMqConfig.TOPIC_QUEUE)注解放在类上,放在方法上便可。

 

死信队列

消息变成死信的几种情况

        * 消息被拒绝(basic.reject / basic.nack),并且requeue = false

        * 消息TTL过期

        * 队列达到最大长度

死信处理过程

        * DLX也是一个正常的Exchange,和一般的Exchange没有区别,它能在任何的队列上被指定,实际上就是设置某个队列的属性。

         * 当这个队列中有死信时,RabbitMQ就会自动的将这个消息重新发布到设置的Exchange上去,进而被路由到另一个队列。

         * 可以监听这个队列中的消息做相应的处理。

示例如下:

创建死信队列

RabbitMQ总结_第19张图片

将正常的队列与死信队列绑定

RabbitMQ总结_第20张图片

当发送消息后,如果消费者拒绝这条消息

RabbitMQ总结_第21张图片

会发现这条消息进入了死信队列

RabbitMQ总结_第22张图片

此时监听死信队列,可以处理死信。

 

 

 

你可能感兴趣的:(消息队列)